strudel music programming language
Table of Contents
- 1. strudel music programming language
- 1.1. Strudel
- 1.2. Strudel + Hydra
- 1.3. Ejemplos
- 1.4. Recursos
- 1.5. Ejemplos no míos
- 1.6. Preguntas sobre strudel
- 1.6.1. Se pueden tener variables?
- 1.6.2. Puede salir del loop?
- 1.6.3. Puedes hacer sidechaining?
- 1.6.4. Se pueden hacer cues?
- 1.6.5. Se puede hacer pre-cueing?
- 1.6.6. Duplicar una secuencia y añadir variaciones
- 1.6.7. Se puede filtrar el bombo?
- 1.6.8. Se pueden generar melodías de bebop con strudel?
- 1.6.9. Puede saltar de un compás a otro? Tiene strudel ligaduaras (ties) ? → legato/clip
- 1.6.10. Se pueden añadir excepciones de manera sencilla?
- 1.6.11. Se puede poner escalas separadas por espacios en mini?
- 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?
- 1.6.14. Se puede hacer ♩ = 120?
- 1.6.15. Se pueden añadir alteraciones?
- 1.7. Undocumented functions
- 1.8. Cadenas concretas
- 1.9. Technical Documents
- 1.10. Resumen mío de strudel
- 1.11. Trucos
- 1.12. Organización de código
- 1.13. Recursos de Strudel
- 1.14. Cosas chulas que le faltan
- 1.15. Links
- 1.16. Tidal
- 1.17. csound
- 1.18. Strudel y OSC
- 1.19. Strudel y MIDI
- 1.20. Ideas y papers
- 1.21. Dar una charla sobre música
- 1.22. Ideas random (utilizar entropía para refactorización automática)
- 1.22.1. Enumeración
- 1.22.2. Segundo plan
- 1.22.3. Primer plan
- 1.22.4. Simplificación → utilizar regresión en funciones de simplificación
- 1.22.5. Primer esbozo
- 1.22.6. Refactorización en haskell
- 1.22.7. Regresión Simbólica en haskell
- 1.22.8. Generar strings mini válidas
- 1.22.9. Symbolic Regression / Genetic Programming
- 1.22.10. Propiedades matemáticas de los operadores de mini
- 1.22.11. Casos sencillos
- 1.22.12. Utilizar PDDL para esto?
- 1.22.13. Inicialmente hacerlo de una única voz
- 1.22.14. Clustering de MIDI (multiples voces)
- 1.22.15. Se pueden utilizar múltiples modelos para elegir el que más conviene (bias vs variance tradeoff)
- 1.22.16. Similar pero para curvas de automatización
- 1.22.17. Cuando esté hecho, añadirle Jointist para hacer transcripción automática
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
- Tipos de flujos
funciones que te transforman el flujo y otros que transforman y duplican - 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()) - 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
https://strudel.cc/?zygkdRLX2IYZ
https://strudel.cc/?RRtS7leYNbUN
Giant Steps refactorized (wip) https://strudel.cc/?a_WMBsCJWPc5 → echarle un vistazo a Puede saltar de un compás a otro? Tiene strudel ligaduaras (ties) ? → legato/clip
https://strudel.cc/?1Y8n19_PQ1eX
https://strudel.cc/?uMTAiOH4K2MQ
Recursos y demás
https://strudel.cc/?AH66mtG3yDH5
https://strudel.cc/?AH66mtG3yDH5- https://strudel.cc/?tsZ-fzbaVjEn
Live Coding TOPLAP
https://strudel.cc/?3fSHrWEV_9ob
Sloow Waater
Waater
https://strudel.cc/?1iTGwTpaYerN
https://strudel.cc/?Uc3UGNUPbKUt
https://strudel.cc/?PkeSVEvTXDCz modificar con envolvente con clip y filtro con lpf paad crear automatizaciones
https://strudel.cc/?3I0j1KJJl3oM I forgot what I was trying to do
La cosa es que funciona
Without csound (I need a way to pass parameters like attack and filter) https://strudel.cc/?ILsEtok_AF_k
Example csound wip https://strudel.cc/?YKXPy-s_emau
Shepard tone? https://strudel.cc/?20N7NyjMEtRY
https://strudel.cc/?KA1IcoQNAnM1 Giant Steps refactorized (wip)
https://strudel.cc/?RvuIJEMjZrXL
https://strudel.cc/?bymSVxtGVlWY Sweet Dreams almost complete → también va un poco lenta la visualización MIDI
Siguiente parte:
https://strudel.cc/?goGJokbhLAit → falta controlar el volumen de las voces que doblan
https://strudel.cc/?DiWHO2mBiui6
Con cambio de escala!
https://strudel.cc/?IaFDcIDd0tcT
https://strudel.cc/?fdLyYzNtkLTS → Falta añadir modulación a menor armónica
https://strudel.cc/?DjJDrAC_1kGz
Parece que sólamente puedes tener un nivel de arrange, no se puede anidar porque es global. timeCat funciona mejor?
https://strudel.cc/?EyCSdE1ley4S
https://strudel.cc/?0q9-7E9SdcKw
https://strudel.cc/?3otaDZGSIzBc
https://strudel.cc/?p4knrUbdHEoo → scale con múltiples valores
https://strudel.cc/?XMVEO-O3fdW0
https://strudel.cc/?-RoyvZ-xpX3W
Para juntarlas las dos tengo que usar arrange
Melodía
https://strudel.cc/?66VRoCsrqykw
https://strudel.cc/?961Im0Alcfai
Parte 2 WIP
https://strudel.cc/?Eu-UaGZZ8zwe
https://strudel.cc/?riU1Er1fEzmJ
Sweeter Dreams
https://strudel.cc/?zz0V-eEBQc9C
csound con lfo que no se resetea
https://strudel.cc/?HeLvMUQO6mH5
Más Sweet Dreams
Meter aleatorizacion a los osciladores para que suene más analógico
https://strudel.cc/?IcVrZXGUgwJn
https://strudel.cc/?PVZEnSPBEUjY
https://strudel.cc/?TwxSUvjPYCVl
https://strudel.cc/?BCZBBFOSImr1
https://strudel.cc/?aNFEma1F7Rn3
Se resetea el lfo si dura menos de una nota, no sé por qué
Sweet Dreams
https://strudel.cc/?o_bHyyAyJ4xK
Sincopado
https://strudel.cc/?JrceGUoyik4A
Piano aleatorio:
https://strudel.cc/?QwBAHINb01Us
Con más estructura:
https://strudel.cc/?xjZiTFc0kRLW
Sin sonido (externos), sesión con https://soundcloud.com/paulluxe
https://strudel.cc/?iws9HhUh6qbZ
Remixeando con ribbon ya como quiero
https://strudel.cc/?Xom9rYczBPTV
https://strudel.cc/?bLktL81CVVyt → pastitas lab
https://strudel.cc/?8RbkOv_cXIrd → disparando pistas al Caustic
https://strudel.cc/?2F6tk2Mg1XQE → midi clock
https://strudel.cc/?Xq8CyzCiVWZ2 → csound con delay. Parece que no puedo poner un delay muy largo porque corta la nota (creo que no lo hace a nivel de sonido, sino que tiene alguna dependencia con la envolvente, o con que estén sonando notas)
Con el patrón que quería!! Quizás puedo hacer alguna función (aquí se ve más claro el patrón que se sigue, es como una lista de números consecutivos y le sumas tus patrones)
https://strudel.cc/?Uv0Pd1AQt-xL
Here Comes The Big One de KNOWER
https://strudel.cc/?GGBFnhZCMQuq (Actualizado https://strudel.cc/?QBFmnas8lB1q)
“Thinking” de Louis Cole
https://strudel.cc/?92-CR_8cc18S
“Lounge sponge” con modulación
https://strudel.cc/?8_jh6sqQzxYO
Mario D’n’B
https://strudel.cc/learn/input-output/?QIVkIiqpL83o
Mario Reggaeton
https://strudel.cc/learn/input-output/?G0b0aNbF6mM1
De 3/4 a 4/4
https://strudel.cc/learn/input-output/?AfhlpdracCST
https://strudel.cc/?W6K49ER__eS3 → csound funcionando
https://strudel.cc/?qUmu11gTuliU Hackmeeting
https://strudel.cc/?SlqVxHQjkNOH
https://strudel.cc/?wf_cNzsv5LX_ → plantilla 909+808
https://strudel.cc/?FlfecbKIHdt4 → plantilla 909
https://strudel.cc/?n0IvEAfprgwv
https://strudel.cc/?gYT6EJnfPkdz
El .euclidRot(3, 8, 0) afecta a lo que pongo en el .superimpose incluso aunque esté antes en la cadena
https://strudel.cc/?cL-E9oHDd9J6
https://strudel.cc/?suptHvhHxOM7
ft. Kronek9
https://strudel.cc/?kG3u2CCuz8Hl
Variaciones D’n’B y hip hop
https://strudel.cc/?yiwbUc_ZFfUS
https://strudel.cc/?L36W0keCx-TP
https://strudel.cc/?MMf6dOsyaj0k ?? del anterior
Tetris con slowcat
https://strudel.cc/?QcfjgP4WMWU0
Tetris en distintos compases (no sé cómo pasar de uno a otro con slowcat)
https://strudel.cc/?3QY2fHb90LDq
D’n’B con samples chopeados al gusto
https://strudel.cc/?-TqXJxfa9aVf
CSound sinte substractivo
https://strudel.cc/?2lUVTy6OYKwK
D’n’B
https://strudel.cc/?By2UqjrTX4j9 → ver si le puedo meter con .slice o .splice para alternar conchooseInWithpick. Representar en pianoroll para saber cuáles son los eventos midi ⇔ número de cacho que tengo que pillar
Ahora con el tempo y los bucles bien hechos
https://strudel.cc/?5jQd3_eIKXQM
Consigo que me salga bien el tempo (teniendo en cuenta que son 7 compases)
https://strudel.cc/?_29qp5pIYQfx
Remixeando con strudel (parece que no se puede cambiar el tempo?)
https://strudel.cc/?rDjAAang41la
https://strudel.cc/?MaqMB7FbhdXT
https://strudel.cc/?ZTMWW9kWMljA
https://strudel.cc/?qN2yU59vN7rm
https://strudel.cc/?TDIOTn1KdliH
Estructuras largas
https://strudel.cc/?CBRMeWF3Qlnd
superimposed someCyclesBy
https://strudel.cc/?pZTxu8egHsVl
No sé lo que he hecho, ni por qué tiene variaciones de volumen → slowcat en vez de layer?
https://strudel.cc/?6mX379h-RScE
Sampleado granular
https://strudel.cc/?SOG0uE3q8N-3
https://strudel.cc/?lsgPl-StumWe (con mejor color)
https://strudel.cc/?xchHuI6im6Pr
https://strudel.cc/?Hj6a_LNt8Tlj
https://strudel.cc/?9ce_ss3wMK6j
- Ejemplo de variables : https://strudel.cc/?b34hJOXUeHUA
- Ejemplo estilo “indio”: https://strudel.cc/?vJmciO7-UCcn
1.4. Recursos
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/?wuueRUkONpfX → await 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
- Missing documentation · Issue #58 · tidalcycles/strudel
- strudel/undocumented.json at main · tidalcycles/strudel
- Las funciones de orden superior no tienen documentación de las funciones que toman como argumento, y en Hap no se sabe de qué tipo pueden ser sus atributos
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.9. Technical Documents
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)
chooseInWithpick 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.13.1. Samples
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.15. Links
1.15.1. Youtube
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.18. Strudel y OSC
1.19. Strudel y MIDI
1.20. Ideas y papers
1.21. Dar una charla sobre música
“Mentiras” sobre las que me voy a basar:
- La frecuencia se descompone en ondas senoidales
- Tenemos 12 notas en la escala
https://www.desmos.com/calculator/hhrf0w56q1 - 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()
- Centrarme en hacerlo interesante. Primero doy algo de teoría, luego algo de práctica, pero lo mantengo balanceado
- Los modos son “selecciones” de estas 12 notas que tienen distintas cualidades. Mostrar como suenan distintas melodías en cada uno de estos modos
- Hacer canciones en disintos modos
- Awesome music theory
https://github.com/vpavlenko/study-music
https://news.ycombinator.com/item?id=35757150
1.22. Ideas random (utilizar entropía para refactorización automática)
1.22.1. Enumeración
- De primeras, lo que hay me sirve para generar datos (estructuras complejas de Tidal), sobre todo la librería de haskell
- Feat: functional enumeration of algebraic types - Haskell12.pdf
- How to enumerate trees from a context-free grammar
- galoisenne/latex/lafi2024/acmart.pdf at master · breandan/galoisenne
- Se pueden enumerar todas las funciones de simplificación?
1.22.2. Segundo plan
Hacerlo en Haskell
- Generar strings en base a gramática: Generating the language of a context-free grammar in Haskell
- Sacar con queries los eventos (notas): https://mzadel.github.io/tidalfundamentals/
- ?? Aprender Haskell ??
1.22.3. Primer plan
- 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 - Producir notas, todas de la misma longitud
https://strudel.cc/?JesRwge1T_f0 - 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.6. Refactorización en haskell
- El grupo de la universidad de St. Andrews y Kent parece el bueno
Christopher Brown1, Huiqing Li2, and Simon Thompson2
1 School of Computer Science, University of St. Andrews, UK
chrisb@cs.st-andrews.ac.uk
2 School of Computing, University of Kent, UK
{H.Li,S.J.Thompson}@kent.ac.uk - Fully funded PhD scholarship: Trustworthy Refactoring Tools for Haskell Programs | Computer Science Blog
- An Expression Processor: A Case Study in Refactoring Haskell Programs | SpringerLink
- HaRe - HaskellWiki
- Thesis.dvi - RefactoringHaskellPrograms.pdf
- RefactoringTools/HaRe: The Haskell Refactoring Tool
- Retrie: Haskell refactoring made easy - Engineering at Meta
- Data-Driven Refactorings for Haskell - Kent Academic Repository
1.22.7. Regresión Simbólica en haskell
- gAldeia/AInet-based-Symbolic-Regression: Haskell implementation of a symbolic regression algorithm. The regression search is done by means of the IT data structure, and the general structure of the algorithm is based on the AInet algorithm (artificial imunne network).
- folivetti/ITEA: Interaction-Transformation Evolutionary Algorithm
- folivetti/srtree: Haskell library for representing and manipulating Symbolic Regression Tree
1.22.8. Generar strings mini válidas
- Mini en strudel está basado en un PEG. Fuente: https://github.com/tidalcycles/strudel/tree/main/packages/mini
- Generating Strings at Random from a Context Free Grammar
- Uniform Random Sampling of Strings from Context-Free Grammar
- Idealmente, en vez de generar strings aleatorias, debería de poder tener en cuenta la estructura de alguna manera
- https://en.wikipedia.org/wiki/Genetic_programming
1.22.9. Symbolic Regression / Genetic Programming
En Javascript más sencillo para que se pueda ejecutar todo en node(?)
- https://github.com/gAldeia/gAldeia.github.io
- https://github.com/zekenie/GPJS
- https://github.com/trevorstephens/gplearn
functions.make_function() factory function. - El problema que tienen estos algoritmos es que aunque puedes definir funciones custom, al final tienen se evalúan todas a un número. Puedo codificar secuencias más largas si lo trato como una secuencia de bits, se me ocurre
Otra manera de codificarlo es como una serie temporal, como una serie de funciones rectangulares - Aquí tendría que definir qué reglas de mutación y demás aplico
- En Rust compilado a Javascript: https://github.com/louisbourque/wasm-genetic-programming/
https://www.louisbourque.ca/wasm-genetic-programming/ - El problema de Symbolic Regression es que no se puede precomputar, sino que siempre hay que hacer la búsqueda completa (no puedes preentrenar un modelo)
Las entradas son los números naturales (hasta un límite para la melodía) y tienen que combinar patrones de manera muy compleja. Normalmente es al revés, combinaciones relativamente simples de bastantes parámetros - Algo más de ML: MMSR: Symbolic Regression is a Multimodal Task - 2402.18603v1.pdf
- Este tipo de algoritmos dan pie a representarlo como árbol de funciones, lo que generaliza mejor para luego aplicarlo al lenguaje completo
- Igual que existe un Symbolic Regression existe un Symbolic Autoencoder?
Why Is Auto-Encoding Difficult for Genetic Programming?
jmmcd/GPAE: Genetic programming auto-encoder
schofield2023genetic.pdf
James McDermott
PonyGE2: Grammatical Evolution in Python
https://github.com/PonyGE/PonyGE2
https://pypi.org/project/evoltree/
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