## Install

Copy `ReaSet.html` and `Sortable.min.js` into REAPER's web root (the folder with `main.js`), then load `Reaset.lua` via **Actions → ReaScript: Load…** and run it once. Add it to your startup actions so it launches with REAPER.

Upgrading from v2.x: replace all three files. Your setlists migrate themselves the first time you open ReaSet with a saved project.

---

# English

## Licence — please read

**ReaSet is now proprietary and free to use.** You may use it for anything, including commercially, on as many machines as you like, and share unmodified copies. Selling it, or distributing modified versions, needs written permission. See [`LICENSE`](../blob/main/LICENSE).

**This is not retroactive.** Versions up to v2.x were GPL v3 and stay GPL v3 for anyone who has them, permanently. You keep every right you had in those versions.

The change was possible because `Reaset.lua` never actually derived from the X-Raym scripts its own header claimed to inherit from — a line-by-line comparison finds 18 identical lines out of 119, and every one of them is `end` or `break`. Zero shared functions. X-Raym's own scripts in `Legacy/` keep their GPL v3 and their authorship. Full evidence in [`docs/RELICENSING.md`](../blob/main/docs/RELICENSING.md).

## Transport — arm, don't detect

The end of a song used to be *detected* by the browser, which then *sent* a stop. That path can never be punctual: ~60 ms poll, plus a 72–107 ms round-trip, plus — the part that took longest to find — **`Main_OnCommand` does not stop the transport on the spot. It stops it on the next audio block.** A reposition landing in that gap runs while the transport is still rolling, so it seeks *playback* into the next song.

- **Auto-stop is now armed in advance.** ReaSet tells REAPER where to stop before it matters, so REAPER stops in its own audio engine at the exact sample and no command travels at the critical moment. Measured on a real region transition: stops 10.7 ms before the boundary, never crossing it. **Fixes the next song being briefly audible at the end of the current one.**
- If arming is not possible (no SWS, for instance) the old detection stays as the fallback. Losing precision is acceptable; losing auto-stop is not.
- **MIDI Init redesigned.** It used to jump to the *start of the next song* and play ~100 ms there, which made that song's opening audible every single time. Now the transport simply starts 5 ms before the song you asked for.
- Repositioning after a stop waits for the transport to **confirm** it stopped, instead of guessing with a timer shorter than the measured round-trip.

## Setlists now live with the project

Setlists used to exist only in the `localStorage` of whichever browser built them. Open ReaSet on the iPad and your show was not there. Clear the browser's data and it was gone.

- **Setlists are now stored in `<project folder>/reaset/setlists/`**, one JSON file each, written by `Reaset.lua`. They travel with the `.rpp`, they can be backed up and versioned, and a human can open and read them.
- **The disk is the only source of truth.** The browser is a client, not a store — so a setlist built on the iPad shows up on the laptop with no merge at all, because both read the same file.
- **If the project cannot be read, ReaSet says so** in the setlist dropdown and blocks saving, rather than letting you edit a phantom copy that will never be written.
- Requires the project to have been **saved at least once**.
- **New `Tools/ReaSet_LibraryDoctor.lua`** (read-only) reports which link broke when a setlist is not saving.

## Fixed

- **Director→Player setlist sync silently dropped most edits.** It triggered on the chunk *count* changing, and toggling a skip flag moves the payload by three characters — so the count stayed identical and the shared file was never rewritten. Players kept showing the old setlist with no sign anything had failed.
- **The web-interface directory was hardcoded**, and on installs where that path is wrong, `io.open` fails silently: the file is never written, the browser gets a 404, and nobody returns an error. Now resolved by finding where `ReaSet.html` actually lives.

---

# Español

## Licencia — leer

**ReaSet pasa a ser propietario y de uso gratuito.** Podés usarlo para lo que quieras, incluso comercialmente, en las máquinas que quieras, y compartir copias sin modificar. Venderlo, o distribuir versiones modificadas, requiere permiso escrito. Ver [`LICENSE`](../blob/main/LICENSE).

**No es retroactivo.** Las versiones hasta la v2.x fueron GPL v3 y lo siguen siendo para quien las tenga, para siempre. Conservás todos los derechos que tenías sobre esas versiones.

El cambio fue posible porque `Reaset.lua` nunca derivó realmente de los scripts de X-Raym que su propia cabecera decía heredar: la comparación línea a línea da 18 líneas idénticas sobre 119, y todas son `end` o `break`. Cero funciones compartidas. Los scripts de X-Raym en `Legacy/` conservan su GPL v3 y su autoría. La evidencia completa está en [`docs/RELICENSING.md`](../blob/main/docs/RELICENSING.md).

## Transporte — armar, no detectar

El fin de una canción lo *detectaba* el navegador, que entonces *mandaba* un stop. Ese camino no puede ser puntual: poll de ~60 ms, más 72–107 ms de ida y vuelta, más —lo que más costó encontrar— **`Main_OnCommand` no detiene el transporte en el acto: lo detiene en el próximo bloque de audio.** Una reposición que caiga en ese hueco se ejecuta con el transporte todavía rodando, así que lleva la *reproducción* a la canción siguiente.

- **El auto-stop ahora se arma por adelantado.** ReaSet le dice a REAPER dónde parar antes de que importe, así REAPER para en su propio motor de audio en la muestra exacta y no viaja ningún comando en el momento crítico. Medido en una transición real de regiones: para 10,7 ms antes del límite, sin cruzarlo nunca. **Corrige que se escuche brevemente la canción siguiente al final de la actual.**
- Si armar no es posible (sin SWS, por ejemplo) la detección vieja queda como respaldo. Perder precisión es aceptable; perder el auto-stop no.
- **MIDI Init rediseñado.** Saltaba al *inicio de la canción siguiente* y reproducía ~100 ms ahí, lo que hacía audible el comienzo de esa canción siempre. Ahora el transporte simplemente arranca 5 ms antes de la canción que pediste.
- La reposición tras un stop espera a que el transporte **confirme** que paró, en vez de adivinar con un temporizador más corto que la ida y vuelta medida.

## Los setlists ahora viven con el proyecto

Los setlists existían solo en el `localStorage` del navegador que los armó. Abrías ReaSet en el iPad y tu show no estaba. Borrabas los datos del navegador y se iba.

- **Se guardan en `<carpeta del proyecto>/reaset/setlists/`**, un archivo JSON cada uno, escritos por `Reaset.lua`. Viajan con el `.rpp`, se pueden respaldar y versionar, y los puede abrir y leer una persona.
- **El disco es la única fuente de verdad.** El navegador es un cliente, no un almacén — así que un setlist armado en el iPad aparece en la laptop sin ningún merge, porque los dos leen el mismo archivo.
- **Si no se puede leer el proyecto, ReaSet lo dice** en la lista de setlists y bloquea el guardado, en vez de dejarte editar una copia fantasma que nunca se va a escribir.
- Requiere que el proyecto se haya **guardado al menos una vez**.
- **Nuevo `Tools/ReaSet_LibraryDoctor.lua`** (solo lectura) informa qué eslabón se cortó cuando un setlist no guarda.

## Arreglado

- **La sincronización Director→Player descartaba en silencio casi todas las ediciones.** Se disparaba con el cambio del *conteo* de chunks, y togglear un skip mueve el payload tres caracteres — así que el conteo quedaba idéntico y el archivo compartido nunca se reescribía. Los Players seguían mostrando el setlist viejo sin ninguna señal de que algo había fallado.
- **El directorio del web interface estaba hardcodeado**, y en instalaciones donde esa ruta no es la correcta `io.open` falla en silencio: el archivo nunca se escribe, el navegador recibe un 404, y nadie devuelve un error. Ahora se resuelve buscando dónde vive realmente `ReaSet.html`.

---

**Known limitation:** Player devices do not yet follow the Director's *active* setlist automatically — see #4.

**Full changelog:** [`CHANGELOG.md`](../blob/main/CHANGELOG.md)
