Skip to content

v2.1.1

Choose a tag to compare

@djenttleman djenttleman released this 28 Mar 01:35
· 90 commits to main since this release
04683ee

ReaSet Changelog


English


v2.1.1 — Playback & Native Loop Fixes

March 27, 2026

Bug fixes

  • Audio glitch at region end with auto-stop enabled — When region A's endpoint matched region B's startpoint, the last ~200ms of A would play back 2–3 times before REAPER processed the stop. Root cause: the transport tick (~33ms) fired the end-of-region command multiple times before receiving a response. Fixed with three coordinated changes:

    • ID-based deduplication (_lastRegionEndTrigger): the trigger fires only once per region and re-arms when the playhead re-enters the region.
    • Time-based lock (_endOfRegionLocked, 400ms): blocks re-triggers while REAPER processes the async stop. Applied across all end-of-region paths: autoStop, shouldPlayNext, and stopAfter.
    • Tighter detection window (timeRem > -0.1 instead of > -1): eliminates false positives when the cursor briefly lands just past the region endpoint.
  • delayAfter path missing lock protection — The delayAfter override sent a STOP but did not activate _endOfRegionLocked, allowing re-triggers during the configured pause when the next region was adjacent. Fixed with the same lock logic as the other paths.

  • Native REAPER loop was incorrectly cancelled — _reaperNativeLoopOff() was called unconditionally before checking activeRegion.loop, tearing down the native loop (Repeat + A/B points) and falling back to the internal JS loop instead. Fixed: if ReaSet_NativeLoop.lua's native loop is active for the current region (_nativeLoopSubId === activeRegion.id), the Lua script already handles the jump and JS no longer intervenes.

  • Stutter + stop when clicking a song with auto-stop enabled — Race condition between the user's click XHR and a stale pending transport response. When a song item was clicked, playRegion dispatched SET/POS + PLAY immediately. If a transport response arrived shortly after showing the old cursor position near the previous song's end (chain=false), the autoStop block dispatched a STOP that REAPER received after the PLAY, killing the new playback. Fixed with the _playRegionLocked flag (500ms): set by playRegion on every user-initiated play, checked by the autoStop block before sending its STOP. The 100ms repositioning setTimeout is also cancelled if the flag is still active.

  • Stutter + stop persistent: triggerMidi() internal STOP kills user playback — The true root cause of the bug. When the previous song's autoStop fires, its 100ms setTimeout calls triggerMidi() to pre-arm MIDI for the next song. triggerMidi() sends PLAY and schedules a STOP 100ms later. If the user clicks a song during that 100ms window, triggerMidi()'s internal STOP arrives at REAPER after the user's PLAY, killing their playback. Fixed by adding the _playbackInitiated flag (500ms), set exclusively by playRegion(), which triggerMidi() checks before sending its internal STOP. A separate flag from _playRegionLocked is required because cueRegion() also sets _playRegionLocked and legitimately needs triggerMidi()'s STOP to fire (to stop REAPER after the brief MIDI-arm play).

  • cueRegion unprotected against autoStop — The cue function (pre-position without playing) did not set _playRegionLocked, so if a stale transport response arrived while the cue was in progress, the autoStop block could send a spurious STOP or fire its 100ms repositioning setTimeout, overwriting the freshly cued position or triggering a duplicate triggerMidi. The same _playRegionLocked (500ms) guard applied to playRegion is now also applied to cueRegion.

  • Boundary marker assigned to both adjacent songs — When a marker was placed exactly at the junction where one song's endpoint coincides with the next song's startpoint, the range check [song.start - ε, song.end + ε] would include it in both songs, generating duplicate nested sections. Implemented _markerBelongsToSong() with semantic disambiguation: (1) if the marker's display name matches a candidate song's display name (case-insensitive), it belongs to that song; (2) "end" keywords (end, fin, outro, salida, cierre, coda, fade, etc.) → belongs to the song ending there; (3) "start" keywords (start, inicio, intro, entrada, apertura, begin, etc.) → belongs to the song starting there; (4) no keyword match → defaults to the song starting there (a boundary marker without exit-keywords most likely names the incoming section).

New file

  • Requirements/ReaSet_NativeLoop.lua — Persistent background script for REAPER. Listens to the ReaSet/nativeLoop ExtState every frame and, on signal, creates a visual region in the timeline, sets native A/B loop points, and enables Repeat. Detects playhead crossings and, after the configured number of passes (loopMax, 0 = infinite), disables Repeat and removes the region. Announces itself to ReaSet automatically via ReaSet/nativeLoopReady=1.

v2.1 — Display, Loop & Command System Overhaul

March 2026

New features

  • Real-time display filters — New "Display Filters" section in the sidebar with independent sliders to adjust Luminance (50–150%), Contrast (50–150%), and Saturation (0–200%) for the setlist body. Values are saved automatically and restored when the app restarts.
  • Fractional loop counter — When a section has +LOOP:N active, a badge shows the current pass as a fraction (e.g. 2/4) to the left of the song action buttons, in the corresponding nested section row, and in Live View next to the section name.
  • Context menu in nested sections — Nested section rows now have their own 3-dot menu (to the left of the name) with access to Notes and Color selection, using the same ID-based override system as songs.
  • Next section indicator in Live — Live View shows the next nested section to the right of the active name, separated by an arrow →. If no section is active but there is an upcoming one, it is also shown as a preview.
  • Secondary progress bar in Live — A second progress bar, thinner and shorter than the main one, reflects progress inside the active nested section. The fill color matches the color assigned to that section in the setlist body.
  • Command system in region names — Region and marker names in REAPER now support a full inline command system to control behavior, appearance, and duration directly from the project, without touching the app. See command reference below.

UI improvements

  • Higher visual contrast — All text, borders, badges, and card backgrounds in the setlist body increased luminance and opacity by 20%. Global variables --text-secondary and --text-subdued were updated accordingly. The transport bar and app background remain unchanged.
  • Live progress bar is now white — The main progress bar fill in Live View changed from green to pure white for better on-stage readability.
  • Drag handle and 3-dot button redesign — The handle has greater weight and contrast. The 3-dot button is now vertical (three SVG dots), with no circular background, and a vertical divider between both elements.
  • SVG icons in context menu — Song options menu icons (Stop, Timer, Notes, Color) replaced emojis with SVG icons consistent with the dark theme.

Bug fixes

  • Context menu did not close when clicking outside — The removeEventListener listener was executed unconditionally on every click, preventing the menu from closing after the first external interaction. Fixed.
  • +LOOP:N played N+1 times — Multiple updatePlaybackUI ticks within the 200ms window before REAPER responded to SET/POS caused duplicate loop-counter increments. Fixed with a dedupe guard _lastLoopFireKey keyed by sectionId:endPos.
  • Real-time loop changes were not applied — The _loopInit flag permanently blocked re-applying default loop state when detecting changes in region name. Replaced with _loopDefault, which tracks the last parsed value and applies changes immediately.
  • Setlists were not isolated per project — Override and setlist data in localStorage was shared across different REAPER projects. A per-project fingerprint (djb2 hash of sorted region IDs) was implemented as a suffix for all storage keys.
  • Keyboard shortcuts blocked typing in notes — Letter keys mapped to app functions were intercepting input while focus was in a <textarea>. Fixed with a tagName guard. Additionally, Enter saves and removes focus, and Escape only removes focus.
  • Secondary Live progress color did not match setlist color — The bar used the region raw color; it now goes through the same getClosestPaletteColor() pipeline as the body, ensuring both colors are identical.

v2.1 Command Reference

Commands are written directly in the region or marker name in REAPER. They can be combined in any order. The clean name left after parsing all commands is what is displayed in the app.

Combined example:

Chorus {dense pre-chorus} +LOOP:4 [green] [.bold] [1:20]

+ commands — Playback behavior

Command Description
+PAUSE Pauses playback when reaching the end of the section.
+SKIP Marks the section as skipped by default. It appears crossed out and with reduced opacity.
+LOOP Enables infinite looping for the section.
+LOOP:N Repeats the section exactly N times, then continues. The X/N badge is shown in real time.
+LOOPFULL Loop with absolute priority: if a song is queued, it waits for the loop to finish before jumping.

Brackets [] — Appearance and duration

Command Description
[color] Assigns a palette color to the card. See available colors below.
[mm:ss] Overrides the displayed section duration (minutes:seconds format).
[nosong] Excludes the item from numbering and total song count. Appears with reduced opacity.
[.class] Applies a custom CSS class to the name. See available classes below.

Available colors:
gray · red · orange · amber · yellow · lime · green · emerald · teal · cyan · sky · blue · indigo · violet · purple · fuchsia · pink · rose

Available CSS classes:

Class Effect
.bold Name in extra bold (900) with adjusted tracking.
.dim Name at 42% opacity.
.italic Name in italic.
.loud Name in uppercase with expanded spacing and maximum weight.

Braces {} — Informational text

Command Description
{text} Displays helper text in italic next to the section name. Not shown in Live View or Canvas. Useful for production notes visible in the setlist.

Special prefixes — Markers

Command Description
> Converts the marker into a section marker (sub-region of the active song).
* Completely ignores the marker; it does not appear in the app.
>>> DestinationName When the section ends, automatically jumps to the region whose name matches DestinationName.

Reserved words

Name Description
STOP Stop marker. Ends playback when reached.
SONG END Alias of STOP.

v2.0 — Nested regions and override system

December 2025

  • Full support for nested regions (sub-sections) with rendering in the setlist body and Live View
  • ID-based override system (g_songOverrides) for custom notes and colors per song and section
  • Live View redesigned with song name, active region, progress bar, and transport controls
  • Canvas Mode for on-stage projection
  • Themed color palette with automatic mapping (getClosestPaletteColor)
  • Grid View mode for quick navigation
  • Support for special markers: +STOP, +PAUSE, +LOOP, +SKIP, [nosong]
  • Export/Import of all app data in JSON

v1.0 — First release

August 2025

  • Web setlist manager for REAPER with WebSocket connection
  • Song play/stop from the UI
  • Drag & drop reordering
  • Real-time playback position sync
  • Multi-setlist support with localStorage persistence

Español


v2.1.1 — Correcciones de reproducción y loop nativo

27 de marzo de 2026

Corrección de bugs

  • Glitch de audio al final de región con auto-stop activado — Cuando el endpoint de la región A coincidía con el startpoint de la región B, el último ~200ms de A se repetía 2–3 veces antes de que REAPER procesara el stop. Causa raíz: el tick de transporte (cada ~33ms) disparaba el comando de fin de región múltiples veces antes de recibir respuesta. Resuelto con tres cambios coordinados:

    • Deduplicación por ID (_lastRegionEndTrigger): el trigger solo se activa una vez por región y se re-arma al volver a entrar a la región.
    • Lock temporal (_endOfRegionLocked, 400ms): bloquea re-disparos mientras REAPER procesa el stop asíncrono. Aplicado en todos los paths de fin de región: autoStop, shouldPlayNext y stopAfter.
    • Ventana de detección acotada (timeRem > -0.1 en vez de > -1): elimina falsos positivos cuando el cursor queda brevemente pasado el endpoint de la región.
  • Path delayAfter sin lock de protección — El override delayAfter enviaba el STOP pero no activaba _endOfRegionLocked, lo que permitía re-disparos durante la pausa configurada cuando la región siguiente era adyacente. Corregido con la misma lógica de lock que los demás paths.

  • Loop nativo de REAPER se cancelaba incorrectamente — La llamada a _reaperNativeLoopOff() ocurría incondicionalmente antes de verificar activeRegion.loop, eliminando el loop nativo (Repeat + A/B points) y cayendo al loop JS interno en su lugar. Corregido: si el native loop de ReaSet_NativeLoop.lua está activo para la región actual (_nativeLoopSubId === activeRegion.id), el script Lua ya gestiona el jump y JS no interviene.

  • Stutter + stop al hacer click en una canción con auto-stop activado — Race condition entre el XHR del click del usuario y una respuesta de transport pendiente con posición antigua. Al hacer click en una canción, playRegion despachaba SET/POS + PLAY inmediatamente. Si en ese mismo momento llegaba una respuesta de transport mostrando la posición anterior cerca del final de la canción previa (chain=false), el bloque autoStop despachaba un STOP que REAPER recibía después del PLAY, deteniendo el playback. Resuelto con el flag _playRegionLocked (500ms): al hacer click se activa, y el bloque autoStop lo verifica antes de enviar su STOP. También se cancela el setTimeout de reposicionamiento de 100ms si el flag sigue activo.

  • Stutter + stop persistente: STOP interno de triggerMidi() mata el playback del usuario — Causa raíz real del bug. Cuando el autoStop de la canción anterior dispara, su setTimeout de 100ms llama a triggerMidi() para pre-armar el MIDI de la siguiente canción. triggerMidi() envía PLAY y programa un STOP 100ms después. Si el usuario hace click en una canción durante esa ventana de 100ms, el STOP interno de triggerMidi() llega a REAPER después del PLAY del usuario, deteniendo el playback. Corregido añadiendo el flag _playbackInitiated (500ms) seteado exclusivamente por playRegion(), que triggerMidi() verifica antes de enviar su STOP interno. Se usa un flag separado de _playRegionLocked porque cueRegion() también setea ese flag y necesita que el STOP de triggerMidi() se ejecute normalmente (para detener el arm de MIDI tras el cue).

  • cueRegion sin protección contra autoStop — La función de cue (preposicionar sin reproducir) no activaba _playRegionLocked, por lo que si llegaba una respuesta de transport estancada con posición antigua mientras el cue estaba en curso, el bloque autoStop podía enviar un STOP adicional o disparar su setTimeout de reposicionamiento, sobreescribiendo la posición de cue recién establecida o ejecutando un triggerMidi duplicado. Aplicado el mismo guard de _playRegionLocked (500ms) a cueRegion.

  • Marcador en límite entre canciones asignado a ambas — Cuando un marcador estaba ubicado exactamente en el punto donde el endpoint de una canción coincide con el startpoint de la siguiente, el check de rango [song.start - ε, song.end + ε] lo incluía en las dos canciones a la vez, generando secciones duplicadas. Implementada la función _markerBelongsToSong() con asignación semántica: (1) si el nombre del marcador coincide con el nombre de display de una de las canciones candidatas, pertenece a esa canción; (2) palabras clave de fin (end, fin, outro, salida, cierre, coda, fade, etc.) → pertenece a la canción que termina ahí; (3) palabras clave de inicio (start, inicio, intro, entrada, apertura, begin, etc.) → pertenece a la canción que comienza ahí; (4) sin coincidencia de palabras clave → se asigna a la canción que comienza (por defecto, un marcador en un límite sin palabras clave de salida probablemente nombra la sección entrante).

Nuevo archivo

  • Requirements/ReaSet_NativeLoop.lua — Script de fondo persistente para REAPER. Escucha el ExtState ReaSet/nativeLoop cada frame y, al recibir la señal, crea una región visual en el timeline, configura los puntos A/B del loop nativo y activa Repeat. Detecta los cruces del playhead y, tras el número de repeticiones configurado (loopMax, 0 = infinito), desactiva Repeat y elimina la región. Se anuncia automáticamente a ReaSet via ReaSet/nativeLoopReady=1.

v2.1 — Overhaul de display, loop y sistema de comandos

Marzo 2026

Nuevas funciones

  • Filtros de pantalla en tiempo real — Nueva sección "Display Filters" en la sidebar con sliders independientes para ajustar Luminancia (50–150%), Contraste (50–150%) y Saturación (0–200%) del cuerpo del setlist. Los valores se guardan automáticamente y se recuperan al reiniciar la app.
  • Contador de loops fraccionario — Cuando una sección tiene +LOOP:N activo, aparece un badge con la vuelta actual en formato fracción (ej. 2/4) a la izquierda de los botones de acción de la canción, en la fila de la sección anidada correspondiente, y en la vista Live junto al nombre de la sección.
  • Menú contextual en secciones anidadas — Las filas de secciones anidadas ahora tienen su propio menú de 3 puntos (a la izquierda del nombre) con acceso a Notas y selección de Color, usando el mismo sistema de overrides por ID que las canciones.
  • Indicador de sección siguiente en Live — La vista Live muestra la próxima sección anidada a la derecha del nombre activo, separada por una flecha →. Si no hay ninguna sección activa pero hay una próxima, también se muestra como anticipo.
  • Barra de progreso secundaria en Live — Una segunda barra de progreso, más delgada y corta que la principal, refleja el avance dentro de la sección anidada activa. El color de relleno coincide con el color asignado a esa sección en el cuerpo del setlist.
  • Sistema de comandos en nombres de región — Los nombres de regiones y marcadores en REAPER ahora admiten un sistema completo de comandos inline para controlar comportamiento, apariencia y duración directamente desde el proyecto, sin tocar la app. Ver referencia de comandos más abajo.

Mejoras de interfaz

  • Contraste visual elevado — Todos los textos, bordes, badges y fondos de tarjetas del cuerpo del setlist aumentaron su luminancia y opacidad en un 20%. Variables globales --text-secondary y --text-subdued actualizadas en consecuencia. La barra de transporte y el fondo de la app permanecen sin cambios.
  • Barra de progreso en Live ahora blanca — El relleno de la barra de progreso principal en Live View cambió de verde a blanco puro para mayor legibilidad en escena.
  • Rediseño del drag handle y botón 3-dot — El handle tiene mayor peso y contraste. El botón 3-dot ahora es vertical (tres puntos SVG), sin fondo circular, con separador vertical entre ambos elementos.
  • Iconos SVG en el menú contextual — Los iconos del menú de opciones de canción (Stop, Timer, Notas, Color) reemplazaron a los emojis por SVGs coherentes con el tema oscuro.

Corrección de bugs

  • Menú contextual no se cerraba al hacer click fuera — El listener removeEventListener se ejecutaba incondicionalmente en cada click, lo que impedía que el menú cerrara tras la primera interacción externa. Corregido.
  • +LOOP:N reproducía N+1 veces — Múltiples ticks de updatePlaybackUI dentro de la ventana de 200ms antes de que REAPER respondiera al SET/POS causaban un incremento duplicado del contador de loops. Resuelto con un guard de deduplicación _lastLoopFireKey clave por sectionId:endPos.
  • Cambios de loop en tiempo real no se aplicaban — El flag _loopInit bloqueaba permanentemente la re-aplicación del estado por defecto del loop al detectar cambios en el nombre de la región. Reemplazado por _loopDefault que rastrea el último valor parseado y aplica el cambio de inmediato.
  • Setlists no aisladas por proyecto — Los datos de overrides y setlists en localStorage se compartían entre proyectos de REAPER distintos. Implementado fingerprint por proyecto (hash djb2 de IDs de región ordenados) como sufijo de todas las claves de almacenamiento.
  • Atajos de teclado bloqueaban la escritura en notas — Las teclas de letras asociadas a funciones de la app interceptaban el input mientras el foco estaba en un <textarea>. Corregido con guard por tagName. Adicionalmente, Enter guarda y quita el foco, y Escape solo quita el foco.
  • Color del progress secundario en Live no coincidía con el setlist — La barra usaba el color raw de la región; ahora pasa por el mismo pipeline getClosestPaletteColor() que el cuerpo, garantizando que ambos colores sean idénticos.

Referencia de comandos v2.1

Los comandos se escriben directamente en el nombre de la región o marcador en REAPER. Se pueden combinar en cualquier orden. El nombre limpio que queda después de parsear todos los comandos es el que se muestra en la app.

Ejemplo combinado:

Chorus {pre-coro denso} +LOOP:4 [green] [.bold] [1:20]

Comandos + — Comportamiento de reproducción

Comando Descripción
+PAUSE Pausa la reproducción al llegar al final de la sección.
+SKIP Marca la sección como omitida por defecto. Aparece tachada y en opacidad reducida.
+LOOP Activa el loop infinito de la sección.
+LOOP:N Repite la sección exactamente N veces y luego continúa. El badge X/N se muestra en tiempo real.
+LOOPFULL Loop con prioridad absoluta: si hay una canción en cola, espera a que el loop termine antes de saltar.

Corchetes [] — Apariencia y duración

Comando Descripción
[color] Asigna un color de la paleta a la tarjeta. Ver colores disponibles abajo.
[mm:ss] Sobreescribe la duración mostrada de la sección (formato minutos:segundos).
[nosong] Excluye el elemento de la numeración y el conteo total de canciones. Aparece en opacidad reducida.
[.clase] Aplica una clase CSS personalizada al nombre. Ver clases disponibles abajo.

Colores disponibles:
gray · red · orange · amber · yellow · lime · green · emerald · teal · cyan · sky · blue · indigo · violet · purple · fuchsia · pink · rose

Clases CSS disponibles:

Clase Efecto
.bold Nombre en negrita extra (900) con tracking ajustado.
.dim Nombre al 42% de opacidad.
.italic Nombre en cursiva.
.loud Nombre en mayúsculas con espaciado ampliado y peso máximo.

Llaves {} — Texto informativo

Comando Descripción
{texto} Muestra texto auxiliar en cursiva junto al nombre de la sección. No se muestra en Live View ni en Canvas. Útil para notas de producción visibles en el setlist.

Prefijos especiales — Marcadores

Comando Descripción
> Convierte el marcador en un marcador de sección (sub-región de la canción activa).
* Ignora completamente el marcador; no aparece en la app.
>>> NombreDestino Al finalizar la sección, salta automáticamente a la región cuyo nombre coincida con NombreDestino.

Palabras reservadas

Nombre Descripción
STOP Marcador de parada. Finaliza la reproducción al alcanzarlo.
SONG END Alias de STOP.

v2.0 — Regiones anidadas y sistema de overrides

Diciembre 2025

  • Soporte completo de regiones anidadas (sub-sections) con visualización en el cuerpo del setlist y la vista Live
  • Sistema de overrides por ID (g_songOverrides) para notas y colores personalizados por canción y sección
  • Vista Live rediseñada con nombre de canción, región activa, barra de progreso y controles de transporte
  • Canvas Mode para proyección en escena
  • Paleta de colores temática con mapeo automático (getClosestPaletteColor)
  • Modo Grid View para navegación rápida
  • Soporte de marcadores especiales: +STOP, +PAUSE, +LOOP, +SKIP, [nosong]
  • Export/Import de todos los datos de la app en JSON

v1.0 — Primera versión

Agosto 2025

  • Setlist manager web para REAPER con conexión WebSocket
  • Reproducción/parada de canciones desde la interfaz
  • Reordenamiento por drag & drop
  • Sincronización de posición de reproducción en tiempo real
  • Soporte multi-setlist con persistencia en localStorage