strudel music programming language

Table of Contents

1. strudel music programming language

https://strudel.cc/ → página principal, documentación, REPL para evaluar código
https://github.com/tidalcycles/awesome-tidalcycles
https://github.com/roipoussiere/jaffle → An hybrid yaml/node editor for Tidal-cycles, based on Strudel.

1.1. Strudel

https://loophole-letters.vercel.app/strudel#pattern-factories

note("<60 63 60 62 62>").euclidRot(5, 8, 1) → te aplica el euclidRot a cada elemento
note("60 63 60 62 62").euclidRot(5, 8, 1) → te aplica el euclidRot a toda la secuencia

// Hay que cargar los samples si quieres usarlos
samples('github:tidalcycles/Dirt-Samples/master');

s("[bd ~]*2, [~ hh]*2, ~ sd")

stack(, , , ) // Te permite apilar sonidos

// Sintaxis:
// Estas tres cosas hacen lo mismo:
note(sequence("60 63 60 62 62"))
(sequence("60 63 60 62 62")).note()
"60 63 60 62 62".sequence().note()

// sometimesBy
// Randomly applies the given function by the given probability
// rarely, sometimes casos particulares de esta función (0.25, 0.5)

pianoroll({minMidi : -100, maxMidi : 90}) // A veces se sale de escala y hay que poner esto a mano

Si pones algo antes de un s("piano"):
  .rarely(add("12"))
Si lo pones después:
  .rarely(add(note("12")))

Parece que el rarely afecta a lo ultimo que hayas puesto

superimpose // Replica lo que haces

EuclidRot
https://github.com/tidalcycles/strudel/blob/main/packages/core/euclid.mjs#L83

Librerías de sonido de la página web:
https://github.com/tidalcycles/strudel/blob/2483bf812f71857c4184ba8964ead9b9e42ac790/website/public/vcsl.json#L825

Me tengo que hacer un resumen
https://strudel.cc/technical-manual/patterns
https://strudel.cc/technical-manual/repl

  1. Tipos de flujos
    funciones que te transforman el flujo y otros que transforman y duplican
  2. Funciones que te convierten de un flujo a otro
    voicing → Turns chord symbols into voicings (notes).
    sound → turns notes into sound
    outside(factor, f, pat) → turns functions that affect notes into functions that affects a level “above”
    return f(pat._fast(factor))._slow(factor);
    notes() no se lleva bien con acordes, te lo limita todo a una nota
    Estas conversiones son reversibles o irreversibles? Puedes cambiar la escala después de haberlo convertido a notas? Creo recordar que no, tenías una manera de acceder “por debajo” (creo que puedes acceder a notas después de haber hecho .notes())
  3. Flujo subterráneo: note te crea un flujo subterráneo

https://loophole-letters.vercel.app/strudel
you can use patterns itself as inputs to pattern methods!

Equivalencias entre funciones y notación mini
https://strudel.cc/functions/intro
https://strudel.cc/learn/factories
Hay algunas que no vienen, por ejemplo chooseCycles ⇔ |
https://strudel.cc/?JuxyQuVJs02L

1.2. Strudel + Hydra

https://strudel.cc/?h_CanGUca0Nq

await initHydra()
H te convierte de strudel a Hydra
Los strings en hydra son listas

Ejemplo de interoperabilidad
https://strudel.cc/?vJ0X96mM0WMQ

1.3. Ejemplos

1.5. Ejemplos no míos

https://strudel.cc/?vHDyO9c6jR1e → poliritmos y revival s
https://strudel.cc/?uTqF1etWMKiN → Avril 14th
https://strudel.cc/?3RqDbR1p_xFc → estructura de canción completa con remix (LilData)
https://strudel.cc/?8IkSnRWSS3eF → breakbeats
https://strudel.cc/?3m0prakJBFF0 → Rhythm of the night
https://strudel.cc/?QPacsmpu9N9t → p y q
puedes marcar patrons con p1, …, p9 sin que estén al final
q1, …, q9 los silencia
all es una función global que afecta a todos los patrones (se puede aplicar sin utilizar p/q poniendolo antes del código), pero afecta al código stackeado y no sé si se pueden separar pistas
https://discord.com/channels/779427371270275082/937365093082079272/1175071709884321852

https://www.youtube.com/watch?v=sNj-I2pZwX8
https://www.youtube.com/watch?v=ji0OS9VqfU0
https://strudel.cc/?pDxmKUzSEPev → utilizar .struct y .mask para crear estructura de la canción. x creo que significa el patrón de entrada
https://strudel.cc/?_5jt76MrrzST → giant steps. Se puede “refactorizar” como ejercicio
https://strudel.cc/?oLxzL4Hexozf → ritmo sincopado
https://strudel.cc/?wuueRUkONpfXawait loadOrc('github:kunstmusik/csound-live-code/master/livecode.orc') para cargar orcs

https://strudel.cc/?6ZdhpWSLZAlW Lenguajes estilo “t” donde “t” es una onda de diente de sierra

1.6. Preguntas sobre strudel

1.6.1. Se pueden tener variables?

1.6.2. Puede salir del loop?

Como tener 2 secciones diferenciadas dentro de un bucle mas grande → con seq, o cat, of TimeCat (alternar entre una y otra) → slowcat es la mejor

  seq(
  n("[0 1 2 3]").fast(4),
  n("[0 1 2 3]").rev().fast(4)
    ).slow(4)
    .scale("C5:minor").piano().pianoroll()

  // !Ojo! que no es lo mismo que esto, si "factorizamos" los .fast(4):
  seq(
  n("[0 1 2 3]"),
  n("[0 1 2 3]").rev()
    ).fast(4).slow(4)
    .scale("C5:minor").piano().pianoroll()

  stack(
  seq(
  n("[0 1 2]").fast(4),
  n("[0 1 2]").rev().fast(4),
    ).slow(4),
  n("[-7 -5]").color("red").slow(4)
    )
    .scale("C5:minor").piano().pianoroll()
1.6.2.1. Se puede congelar un loop? (pick, inhabit, ribbon)

Es decir, centrarse en una sección del loop.
Por ejemplo quedarse fijo repitiendo la primera/segunda/tercera/cuarta parte de un bucle de longitud 4
si reemplazas un slowcat por un chooseCycles, lo hace pero de manera aleatoria
Se puede hacer con chooseInWith pick, que necesita que los patrones siguientes estén en formato lista al contrario de slowcat
pick is similar, but with inhabit you can name the patterns
Luego inhabit se estandarizó y ya no hace falta esto

chooseInWith("<0 1>".div(2),
          [sequence("[a3 c4]")
          ,sequence("[e4 g4]")
        ]).add("<0 2 3 1>").note()

pick("<0 1>",
          [sequence("[a3 c4]")
          ,sequence("[e4 g4]")
        ]).add("<0 2 3 1>").note()

const inhabit = register('inhabit', function (lookup, pat) {
  for (const key of Object.keys(lookup)) {
     lookup[key] = reify(lookup[key]);
  }
  return pat.fmap(x => lookup[x] ?? silence).innerJoin();
});

"<a b>".inhabit(
          {a: sequence("[a3 c4]")
          ,b: sequence("[e4 g4]")
        }).add("<0 2 3 1>").note()

let a = s("bd");
let b = s("hh*2");
let c = inhabit({a,b}, "<a b>",);

inhabit({a,b,c},"<a b c>").bank('RolandTR909').fast(4)
// inhabit({a,b,c},"<a b <a b>>").bank('RolandTR909').fast(4) // Equivalent pattern

https://strudel.cc/?3a775aCYwWQf (slowcat)
https://strudel.cc/?zX7JcqtLm2l_ (chooseInWith pick) → no sé por qué el slow pasa de ser 16 a ser 2
Hay alguna manera de pasar de trocear un mini en cachos (convertirlo en Array de mini) para pasárselos a chooseInWith pick? chunk?

https://club.tidalcycles.org/t/loop-or-freeze-cycles/2773/2

ribbon es la función!!

.restart(“<x@16>”) → esto es, pero sólo repite el inicio
.early(8).restart(“<x@4>”) con late indexas lo que quieres buscar

1.6.3. Puedes hacer sidechaining?

Modular parámetros de una pista con parámetros de otra
Por ejemplo sidechaining es bajar el volumen de una pista cuando suena otra
https://forum.toplap.org/t/sidechain-in-tidal/1016 tidal parece que sí
De todas maneras esto es un poco un “hack”, y hacerlo por midi te da más control

1.6.4. Se pueden hacer cues?

Se puede usar una lista global para los niveles de cada pista.
Se podrían hacer cues con esta estructura, es decir, activarla y que no hiciese nada hasta el siguiente bucle?

Tidal tiene esto:
https://tidalcycles.org/docs/reference/transitions/

1.6.5. Se puede hacer pre-cueing?

Tener otra pista que suena por otro canal distinto para ir haciendo pruebas, o aplicar una función en un canal de audio sólamente, para ver cómo va a quedar (y si va a dar un error de sintaxis!)
https://github.com/tidalcycles/strudel/releases/tag/v1.1.0 → Clock sync between multiple instances
Con esto sí que se puede

1.6.6. Duplicar una secuencia y añadir variaciones

 stack(
seq(
n("[0 1 2]").fast(4),
n("[0 1 2]").rev().fast(4),
  ).slow(4),
 n("[-7 -5]").color("red").slow(4)
  )
  .add("[0 -2 -4 -3]/16") // ← esto es lo que queremos ampliar
  // .add("[0 -2 -4 -3 0 -2 -4 -3]/32") // ← esto es lo mismo, pero tenemos más libertad
  // .add("[0 -2 -4 -3 0 -2 -4 -3 0 -2 -4 -3 0 -2 -4 -3]/64") // ← de nuevo lo mismo, más libertad
  // se podría hacer hasta una macro de vim para editar esto, yi[t/i <ESC>pf/lciw
  .scale("C5:major")
  .piano().pianoroll()

1.6.7. Se puede filtrar el bombo?

function filterKick(x) {
  return stack(...x.filter(x=>!x.value.s.includes('bd')));
}

stack(
  .
  .
  .
).arpWith(filterKick)

// Más sencilllo
stack(
  .
  .
  .
).filterHaps(x=>!x.value.s.includes('bd'))

Funciona a cualquier nivel porque trabaja a cualquier nivel (puedes meter stacks dentro de stacks y aun así los filtra)
Si quieres quitar el bombo una de cada 32*4 = 128 veces:

.someCyclesBy("<0 1>/32/4", y=>y.filterHaps(x=>!x.value.s.includes('bd')))

https://strudel.cc/?9fB3wx3kuSwh

const strt = "<0 1>/4";
stack(
  stack(
    s("[bd bd ~ ~ [~ bd] bd ~ ~ ]/4"),
    s("[hh]*4").velocity(0.5),
    s("[~ sd]/2"),
    ).bank('RolandTR909').fast(2),
)
.someCyclesBy(strt, y=>stack(y.filterHaps(x=>!x.value.s.includes('bd')),
                                  s("cr*4").bank('RolandTR909').struct(strt) // En teoría este struct no debería de ser necesario, es igual que el primero
                                 )
             )

1.6.8. Se pueden generar melodías de bebop con strudel?

Dada una secuencia de acordes, generar escalas
Dada una secuencia de escalas, generar una línea
Se me ocurre hacer un choose aleatorio, pero sería “demasiado aleatorio” y habría que constreñirlo un poco más, como que por ejemplo no haga saltos muy grandes. Se puede constreñir en strudel? Se pueden utilizar los voicings para esto?
Una buena idea es comprimir en un acorde la secuencia, y luego expandirla con .arp
renderVoicing quizás es una manera mejor de hacer esto porque no te hace el stack
Estadística de transiciones de notas
Generación aleatoria
https://strudel.cc/?ENbS7h4ebnjK

1.6.9. Puede saltar de un compás a otro? Tiene strudel ligaduaras (ties) ? → legato/clip

`[[b4 [e4 g#4 b4]] [~ c5] d5 e5]`.note()

Si quiero que “[e4 g#4 b4]” ocupe el ~ que tiene después, no sé cómo hacerlo. Es como una ligadura?

`[[b4 [e4 g#4 b4]] [b4 c5] d5 e5]`.note()

note("c3 eb3 g3 c4").legato("<.25 .5 1 2>")

https://github.com/tidalcycles/strudel/issues/52

https://club.tidalcycles.org/t/dotted-notes-and-patterns/4971
Pregunta:
https://strudel.cc/?nTbrkqN9d_ba
Respuesta:
https://strudel.cc/?NfRhJu5NSOUU
Operador as:
https://strudel.cc/?9Vx2l3N8UIJF

1.6.10. Se pueden añadir excepciones de manera sencilla?

Se me ocurre hacer algo parecido a lo del bombo, que tenga algún tipo de inteligencia y me filtre los eventos que quiero y me aplique cosas extrañas. Por ejemplo cambiarle el patrón rítmico a una pista únicamente, o cambiarle la escala en un momento determinado, etc
Diccionarios con valores por defecto, para que sea fácil de cambiar en un caso especifico sin generar cambios en cascada

1.6.11. Se puede poner escalas separadas por espacios en mini?

https://strudel.cc/?cqkc2h2GLql7

setcps(1)
const lead =  n("<-2 -3 -7 -5>/2".add(14)
                .superimpose(x=>x.add(2).clip(1.05))
               ).layer(x=>x.clip(1.05));


lead

  // .scale("<C3:minor C3:harmonic_minor C3:minor C3:minor >/2")
  .scale(slowcat('C minor', 'C harmonic minor', 'C minor', 'C minor').slow(2))
  .sound('gm_voice_oohs')
  .lpf(3000)
  .pianoroll()

1.6.12. Se puede hacer el son de clave con ritmos euclidianos?

1.6.13. Hay un sitio donde estén todos los tipos de baterías que hay?

For drum sounds, strudel uses the comprehensive tidal-drum-machines library, with the following naming convention:
https://github.com/geikha/tidal-drum-machines/tree/main/machines
https://github.com/sgossner/VCSL

1.6.14. Se puede hacer ♩ = 120?

El equivalente sería “[x]” = 120, de tal manera que todos los “x” que están a un cierto nivel de anidación de corchetes/llaves duran lo mismo
Parece que lo que es fijo es el ciclo más que la longitud de la nota y puede ser lioso

1.6.15. Se pueden añadir alteraciones?

Con inhabit se pueden hacer cosas, definir tu propio lenguaje
Con el operador as puedes definir modificaciones a notas

1.7. Undocumented functions

1.8. Cadenas concretas

stack te sirve para apilar instrumentos que suenen a la vez, seq para alternar secuencias

n parece que se puede utilizar indistintamente con ""
n → .add → .scale → .piano
stack(seq( n → .add → .scale → .piano ))
chords("") → .dict('lefthand') → .voicing → .struct
"" → .scale('C minor') → .note()

1.10. Resumen mío de strudel

1.11. Trucos

Puedes utilizar attack grande para silenciar determinado tipo de pistas:
https://strudel.cc/?ofyg0O00dlam → De paso, es un buen ejemplo para empezar a mezclar algo ya hecho

1.11.0.1. Transiciones sincronizadas

.mask(0) .mask(“~”) eliminan una nota
Con ellas o con .gain se pueden utilizar para silenciar y activar a la vez pistas

1.11.0.2. Cambiar el tempo de manera global
// A veces con .cpm no es suficiente, y hay que utilizar setCps
setCps(2*0.5625); // Valor por defecto
setCps(2*0.7);

Si estás trabajando con un tempo global, sin sincronización MIDI, es mejor hacer un .fast/.slow global porque es más preciso que un setCps, creo que por problemas de precisión en javascript

1.11.0.3. Cómo organizar en pistas?

Por ejemplo cuando tengo que aplicar cambios a patrones, no sé si tener una matriz y luego hacer chooseInWith, o un when o algo…
La cosa es aplicar una función en cada momento, y unas veces puede ser la identidad, una función que no suene, o cosas mas complicadas
Idealmente podrías tener una matriz 3d que sean tiempo (X), pistas (Y) y capas(Z)
O en cada celda 2x2 puedes tener una suma de sonidos apilados

function I(...args) { return args}

También puedes verlo como 2 tipos de matrices, una más lenta y otra más rápida. Ejemplo:

Con más estructura:
https://strudel.cc/?xjZiTFc0kRLW

1.11.0.4. Lista que siempre vale 0 excepto para valores concretos
mini('[' + [...Array(8).keys()].map(x=>0).join(' ') + ']');
signal(x=>x>4 && x<5? 1:0)

Quizás proponer un operador en notación mini que sea un puntillo como . para cuando quieres aplicarlo a clip para hacer ligaduras

1.11.0.5. Visualizaciones pequeñas de pianoroll y scope (como por pista)
_pianoroll() y _scope()

1.12. Organización de código

// Esto también es lo mismo:
"[0 2]"
.fast(2)
.add("7")
.add("<0 -1.25 -2 -1>")
.scale("C minor")

note(scale('C minor', add("<0 -1 -2 -1>", add(7, fast(2, "[0 2]")))))
  • Si quieres añadir una cosa a una cadena, utilizas métodos
  • Si quieres hacer otra cosa distinta, utilizas llamadas a funciones (stack por ejemplo), separando las “pistas” por comas
  • Una lista/diccionario global para los niveles de cada pista, o cada “parte” coherente o “pista”. Lo que aporta es poder silenciar y activar transiciones de una vez
  • orbit es como unas tags numéricas para que compartan los mismos efectos
  • cut es un grupo de percusion (open/closed hihats por ejemplo)
  • chooseInWith pick te da opciones para poder cambiar la música de manera fácil mientras estás programando otra cosa
  • arrange te permite hacer secciones que duran más de un loop cada una (también timeCat y quizás más estable)
  • pure te permite hacer listas de cosas sin saber como combinarlas.
    fmap(x => stack(…x)).innerJoin() te hace un stack de lo que haya sin conocimiento de lo que hay dentro
    fmap(x => slowcat(…x)).innerJoin() te hace un slowcat de lo que haya sin conocimiento de lo que hay dentro
  • Hay una secuencia más lenta que dicta toda la canción, grupos de 4, 8, 16 compases
  • No suelen mezclarse tonalidades/escalas (aunque puede que no sea cierto para pentatónicas por ejemplo). Por lo tanto, se suele tener una escala global que va cambiando, estableciendo un ritmo armónico
  • De ese ritmo armónico surgen un stack() de distintas pistas, que luego se pueden agrupar entre ellas con pequeños stack (por ejemplo la batería). No queremos convertirlas en Pattern de primeras para mantener jerarquía y flexibilidad
  • Se puede utilizar un slowcat() para alternar entre distintas partes (stutter edit con distintas pistas)

1.12.1. Utilizar siempre pure (agnóstico de qué estructura va a tener)

Un Pattern de Patterns es un Pattern (es completamente plano). Puedes conseguir estructura mediante pure:

pure([note("d e a d"),
      note("f g c f"),
      pure([s("bd*2"), s("~ sd")])
      ])
  .fmap(x=>stack(
    x[0],
    slowcat(x[1], x[0].s("sawtooth")),
    x[2].fmap(y=>stack(...y)).innerJoin())
       )
  .innerJoin()
// También puede ser un diccionario
pure({dead: note("d e a d"),
      fgcg: note("f g c f"),
      drums: pure([s("bd*2"), s("~ sd")])
      })
  .fmap(x=>stack(
    x['dead'],
    slowcat(x['fgcg'], x['dead'].s("sawtooth")),
    x['drums'].fmap(y=>stack(...y)).innerJoin())
       )
  .innerJoin()

1.13. Recursos de Strudel

https://strudel.cc/learn/csound
Failed to fetch dynamically imported module: https://strudel.cc/_astro/csound.f280dac2.js → hard reload clearing cache and it should work
https://github.com/kunstmusik/csound-live-code/blob/main/doc/cheatsheet.md

await samples('github:tidalcycles/Dirt-Samples/master/')
samples({
  bd: 'bd/BT0A0D0.wav',
  sn: 'sn/ST0T0S3.wav',
  hh: 'hh/000_hh3closedhh.wav'
}, 'https://loophole-letters.vercel.app/samples/tidal/')

await loadOrc('github:kunstmusik/csound-live-code/master/livecode.orc')

await loadCsound`
código de csound
`

https://strudel.cc/?Xa0Sa8bVTh6T

http://abbernie.github.io/tune/scales.html → todas las escalas

1.14. Cosas chulas que le faltan

Input/output de audio/midi como norns o zynthian. “Grabar” un patrón MIDI y que suene al instante (convertir al vuelo en notación mini, cuantizando?)
Se puede incluir feedback (en sentido amplio)? Que las salidas vuelvan a ser entradas
te puede hacer de mezclador de entradas de MIDI y/o audio?
Con Tidal sí que se puede MIDI y audio

Timestretching/pitchshifting es muy practico para granulsr

https://tidalcycles.org/docs/configuration/MIDIOSC/midi/
https://club.tidalcycles.org/t/live-recording-into-tidal-getting-audio-input-working/2695

1.16. Tidal

https://mzadel.github.io/tidalfundamentals/
https://tidalcycles.org/docs/getting-started/tidal_start

sudo pacman -S haskell-tidal
include("SuperDirt");
// reiniciar
SuperDirt.start

ghci -ghci-script /usr/share/haskell-tidal/BootTidal.hs

https://github.com/grddavies/tidal.nvim parece el fork más avanzado

1.17. csound

1.21. Dar una charla sobre música

“Mentiras” sobre las que me voy a basar:

  1. La frecuencia se descompone en ondas senoidales
  2. Tenemos 12 notas en la escala
    https://www.desmos.com/calculator/hhrf0w56q1
  3. Las terceras y las quintas son la manera más “natural”/“sencilla” porque aparecen en la serie armónica
freq(
  "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16"
  .fmap(n => 110 * n)
).slow(4).s("sine").pianoroll()

1.22. Ideas random (utilizar entropía para refactorización automática)

1.22.1. Enumeración

1.22.2. Segundo plan

Hacerlo en Haskell

  1. Generar strings en base a gramática: Generating the language of a context-free grammar in Haskell
  2. Sacar con queries los eventos (notas): https://mzadel.github.io/tidalfundamentals/
  3. ?? Aprender Haskell ??

1.22.3. Primer plan

  1. Utilizar PonyGE2: Grammatical Evolution in Python https://github.com/PonyGE/PonyGE2 para buscar con
    Convertir Mini (notación PEG) https://github.com/tidalcycles/strudel/tree/main/packages/mini a BNF que es lo que acepta PonyGE2
    Puede estar interesante quitar ciertas instrucciones para generar modelos más sencillos y que tarde menos ( se pueden comentar con # pero hay que asegurarse de que no se rompe nada)
    Las reglas de generación tienen que ser lo más sencillas posibles (no tienen que aceptar todo el código válido) para reducir el espacio de búsqueda: luego se puede expandir con un formatter en una forma más bonita
  2. Producir notas, todas de la misma longitud
    https://strudel.cc/?JesRwge1T_f0
  3. Generar Train y Test
    PonyGE2 necesita conjuntos de train y test, pero no está bien definido cuáles son las variables independientes en este caso, incluso diría que no tiene ninguna. Una opción natural parece pasarle la longitud de esa nota como única variable, o incluso mejor la longitud que le queda a esa nota en ese momento, a modo de “predicción” de cuando va a terminar. También puedo entrenarlo múltiples epochs, es decir, repitiendo patrones para generar más datos: no me preocupa sobreajustar a los datos, sino que es justo lo que busco. Train y Test pueden ser iguales por la misma razón
    Los números 1|2|3|4 … son variables independientes o expresiones terminales del lenguaje?
    Para pasarle sólo un ciclo como conjunto de train/test necesito detectar qué es un ciclo: https://en.wikipedia.org/wiki/Cycle_detection#Algorithms pero estos algoritmos podrían detectar un subciclo en vez de un ciclo (en general no saben cuándo parar)

1.22.4. Simplificación → utilizar regresión en funciones de simplificación

Puedo utilizar la simplificación de expresiones estilo CAS (Computer Algebra System)
Automated simplification of large symbolic expressions
Understanding Expression Simplification
Macsyma’s General Simplifier: Philosophy and Operation
Lo que sí que tiene más sentido es utilizar regresión simbólica con qué simplificaciones aplicar y en qué orden. El árbol ahí ya es plano y no crece tanto

1.22.5. Primer esbozo

  • Un objetivo más simple puede ser refactorizar el lenguaje mini
    No está claro si el lenguaje tiene las mismas propiedades que Haskell porque está dentro de una string. Por ejemplo puede que chooseCycles | no se pueda utilizar porque no es determinista
  • https://trevanhaskell.com/projects/entropy/
    Entropía de lenguajes de programación
  • Utilizar esta entropía para seleccionar entre dos construcciones iguales (que dan la misma salida). Creo que Tidal Cycles es mejor que strudel
  • Dos expresiones en un lenguaje “declarativo” (mini/strudel/tidal) se define como que produzcan el mismo MIDI
    Este concepto es Observational Equivalence
  • Creo que el problema puede tener que ver con “pretty printing”, convertir de un árbol a texto sería pasar de una estructura de código a MIDI, o a una representación nota a nota
    prettier.pdf
  • https://en.wikipedia.org/wiki/Kolmogorov_complexity
  • Hacerlo relativo a la primera nota, para que tenga simetría de transposición/traslación. Para ello puedo hacer que el código mini se ejecute por ejemplo siempre en un .add()
    Aun así no expresa todas las simetrías que quiero, porque por ejemplo puedo sumarle o restarle un número constante y me sigue quedando lo mismo.
    Puedo hacer la derivada entre valores consecutivos de eso y creo que ya tiene las simetrías que quiero, pero entonces no sé cómo representarlo en strudel (tendría que “integrarlo” antes de pasarlo a strudel, o tendria que “integrarlo” en strudel)
    https://strudel.cc/?Vpi01bcTw7_J
  • Las reglas que defina de igualdad son las que definen el mecanismo de optimización: no siempre resultan en un objetivo que reduce la entropía, pero siempre se pueden aplicar, y ese espacio de posibles aplicaciones de reglas que dan el mismo resultado es el espacio de búsqueda. Puedo buscarlas yo o puede buscarlas un algoritmo de prueba de teoremas (?) Cómo asegurarse de que las transformaciones que descubres son exhaustivas?
    Siempre se empieza con seq(n1 n2 n3 n4 …)
    Como mucho puedes tener un árbol de funciones arbitrarias compuesto de cada nota individual

1.22.9. Symbolic Regression / Genetic Programming

En Javascript más sencillo para que se pueda ejecutar todo en node(?)

1.22.10. Propiedades matemáticas de los operadores de mini

Los operadores de sonido en paralelo y en secuencial de strudel tienen propiedades distributivas como producto y suma
“a b,c d” = “<a,c> <b,d>”
El elemento neutro de “,” sería “~” y el de “ ” sería “” (string vacía)
https://strudel.cc/?Sd0AqY_doVWa
“<a b,c d>*2” = “<a,c> <b,d>”
https://strudel.cc/?2yHX6HbiwP7R Hay un desajuste de velocidad por el alineamineto de patrones

1.22.11. Casos sencillos

Parto de una secuencia seq(n1 n2 n3 n4 …)
Luego seq(n1 n4 n8 n16 …).add(seq(t1 t2 t3 t4 … ).slow(4))

1.22.12. Utilizar PDDL para esto?

Modelar el lenguaje mini y las maneras en las que tienes operaciones que son equivalentes

Es interesante porque pasas de una búsqueda sin tener información de la estructura a una especie de planificación en la que tienes información de la estructura interna

1.22.13. Inicialmente hacerlo de una única voz

1.22.14. Clustering de MIDI (multiples voces)

Para poder identificar distintas voces. Creo que tiene que haber cosas ya hechas con inteligencia artificial

1.22.15. Se pueden utilizar múltiples modelos para elegir el que más conviene (bias vs variance tradeoff)

  • Cuantizar más o menos de entrada
  • Utilizar más o menos números en la BNF

1.22.16. Similar pero para curvas de automatización

Smoothing is the quantization of manual knob modulation
Lo de renardo pero en sentido inverso, cambiar algo (midi, cc) en el daw y que se refleje en el código, como una variación global o como una variación de ese patrón en concreto, o con los patrones que cumplan una condición (los pares, uno de cada cuatro, etc)
Un suavizado te deja ponerlo en términos de funciones más matemáticamente simples, y luego podrías utilizar la misma estrategia para simplificarlo
Es algo parecido a expresar una curva en series de fourier o en otro tipo de bases. Por ejemplo esto:
https://github.com/MilesCranmer/PySR

1.22.17. Cuando esté hecho, añadirle Jointist para hacer transcripción automática

Audio → Jointist → Modelo → Código Mini / Strudel / Tidal

Author: Julian Lopez Carballal

Created: 2024-11-06 mié 13:07