diff --git a/public/system-files/Documentation.orc b/public/system-files/Documentation.orc new file mode 100644 index 0000000..6d8021a --- /dev/null +++ b/public/system-files/Documentation.orc @@ -0,0 +1,477 @@ + + + + _ _ _ _ _____ _____ _ _ _____ _ +| | (_) | (_) / __ \/ __ \| \ | | / __ \ | | +| | ___ _____ ___ ___ __| |_ _ __ __ _ | / \/| / \/| \| | | / \/ ___ __| | ___ +| | | \ \ / / _ \ / __/ _ \ / _` | | '_ \ / _` | | | | | | . ` | | | / _ \ / _` |/ _ \ +| |___| |\ V / __/ | (_| (_) | (_| | | | | | (_| | | \__/\| \__/\| |\ | | \__/\ (_) | (_| | __/ +\_____/_| \_/ \___| \___\___/ \__,_|_|_| |_|\__, | \____/ \____/\_| \_/ \____/\___/ \__,_|\___| + __/ | + |___/ + + + ; Ce document contient toutes les informations sur les fonctions (mots) utilisables + ; dans la plateforme de livecoding. + ; Vous pouvez copier-coller les exemples dans le fichier "livecode.orc". + + + _ +| | +| |_ ___ _ __ ___ _ __ ___ +| __/ _ \ '_ ` _ \| '_ \ / _ \ +| || __/ | | | | | |_) | (_) | + \__\___|_| |_| |_| .__/ \___/ + | | + |_| + + + ; Pour modifier le tempo, il faut utiliser la fonction de tempo + ; Par défaut, le tempo est de 1, ce qui signifie 1 pulsation par seconde + ; Si on met 2, la pulsation est donc de ... 2 pulsations par secondes + tempo(2) ; [0.001 - 10] + ; La modification du tempo modifiera la vitesse du rythme de TOUS les sons en train d'être joués + + ; Un attracteur chaotique gère d'autres paramètres du temps, on peut changer sa vitesse en écrivant + ; Il influence notamment la vitesse d'évolution des modes "sauvages" (voir les Sons) + chaos_speed(4) ; [0.1 - 100] + + ; Pour entendre le tempo, on peut utiliser le metronome (sans accent ici pour éviter l'erreur) + metronome + + + _____ +/ ___| +\ `--. ___ _ __ ___ + `--. \/ _ \| '_ \/ __| +/\__/ / (_) | | | \__ \ +\____/ \___/|_| |_|___/ + + ; "ping" - son artificiel percussif brillant + ; Paramètres : + ; - Rythme + ; - Volume [0-1] + ; - Hauteur (note) [0-100] + ; - Durée [>0] + ping(beat(4), 0.3, 50, 1/4) + + ; "buzzy" - Un son plus doux que Ping et dont le rythme interne peut être imprévisible + ; Paramètres : + ; - Rythme + ; - Volume [0-1] + ; - Hauteur (note de base de l'accord) [0-100] + ; - Durée [>0] + buzzy(beat(1/4), 0.5, 50, 4) + + ; "glidy" - un son qui glisse vers la note choisie + ; - Rythme + ; - Volume [0-1] + ; - Hauteur (note de base de l'accord) [0-100] + ; - Durée [>0] + glidy(beat(1), 0.5, 60, 1) + + ; "darkwave" - une vague sombre, qui gronde quand elle devient forte + ; - Rythme + ; - Volume [0-1] + ; - Hauteur (note de base de l'accord) [0-100] + ; - Durée [>0] + darkwave(beat(1/4), 0.6, 30, 4) + + ; "brightwave" - une vague brillante et lumineuse + ; - Rythme + ; - Volume [0-1] + ; - Hauteur (note de base de l'accord) [0-100] + ; - Durée [>0] + brightwave(beat(1/4), 0.6, 30, 4) + + ; "buzzwave" - une vague qui fait bzzz + ; - Rythme + ; - Volume [0-1] + ; - Hauteur (note de base de l'accord) [0-100] + ; - Durée [>0] + buzzwave(beat(1/4), 0.6, 30, 4) + + ; "noisywave" - une vague avec un bruit de fond, comme l'écume de la mer + ; - Rythme + ; - Volume [0-1] + ; - Hauteur (note de base de l'accord) [0-100] + ; - Durée [>0] + noisywave(beat(1/4), 0.6, 30, 4) + + ; "crunchy" - un son percussif qui croustille + ; - Rythme + ; - Volume [0-1] + ; - Hauteur (note de base de l'accord) [0-100] + ; - Durée [>0] + crunchy(beat(4), 0.6, 20, 1/4) + + ; Strike : un son percussif et très saturé (distordu, sauvage) + ; Paramètres : + ; - Rythme + ; - Volume [0-1] + ; - Hauteur (note) [0-100] + ; - Durée [>0] + strike(beat(1), 0.9, 8, 1) + + ; Smooth : un son doux, assez rond + ; Paramètres : + ; - Rythme + ; - Volume [0-1] + ; - Hauteur (note) [0-100] + ; - Durée [>0] + smooth(beat(1/3), 0.5, 20, 4) + + ; Bounce : un son qui rebondit et qui brille ! + ; Paramètres : + ; - Rythme + ; - Volume [0-1] + ; - Hauteur (note) [0-100] + ; - Durée [>0] + bounce(beat(1/2), 0.5, 30, 3) + +______ _ _ _ _ _ +| ___| | | (_) | | | | | | +| |_ ___ _ __ ___| |_ _ ___ _ __ ___ __| | ___ _ __ _ _| |_| |__ _ __ ___ ___ +| _/ _ \| '_ \ / __| __| |/ _ \| '_ \/ __| / _` |/ _ \ | '__| | | | __| '_ \| '_ ` _ \ / _ \ +| || (_) | | | | (__| |_| | (_) | | | \__ \ | (_| | __/ | | | |_| | |_| | | | | | | | | __/ +\_| \___/|_| |_|\___|\__|_|\___/|_| |_|___/ \__,_|\___| |_| \__, |\__|_| |_|_| |_| |_|\___| + __/ | + |___/ + + + ; "beat" - pulsation régulière + ;Paramètres : + ; - Vitesse (par rapport au tempo) [> 0] + beat(2) // 2 pulsations par temps + + ; "swing" - pulsation régulière + une pulsation swing + ; Paramètres : + ; - Vitesse (par rapport au tempo) [> 0] + ; - Swing [0 - 1] + swing(2, 0.75) + swing(2, 3/4) + + ; "rhythm" - rythme écrit sous la forme d'une liste + ; Paramètres : + ; - liste de 1 et 0 sous forme d'un "array" + ; - Vitesse (par rapport au tempo) [> 0] + rhythm(array(1, 0, 0, 1, 0, 0, 1, 1 ), 1) + ; Peut aussi s'écrire + rhythm( + array(1, 0, 0, 1), + 1) + + ; "drunk" - rythme aléatoire mais quand même lié au tempo + ; Paramètres : + ; - Vitesse (par rapport au tempo) [> 0] + ; - Taux d'aléatoire [0 - 1] + drunk(2, 0.8) + + ; "euclidian" - rythmes basés sur la division euclidienne + ; (voir https://dbkaplun.github.io/euclidean-rhythm/) + ; Paramètres : + ; - Vitesse [> 0] + ; - Nombre de "steps" [> 0] + ; - Division du cercle [> 0] + ; - Rotation [0 - 1] + euclidian(2, 3, 7, 0) + + ; "chaos_rhythm" - rythmes basés sur l'horloge chaotique principale + ; Paramètres + ; - Choix de l'horloge [1, 2 ou 3] + ; - Seuil de détection [0 - 1] + chaos_rhythm(1, 0.6) + + +______ _ _ _ _ _ +| ___| | | (_) | | | | | | +| |_ ___ _ __ ___| |_ _ ___ _ __ ___ __| | ___ ___ ___ _ __ | |_ _ __ ___ | | ___ +| _/ _ \| '_ \ / __| __| |/ _ \| '_ \/ __| / _` |/ _ \ / __/ _ \| '_ \| __| '__/ _ \| |/ _ \ +| || (_) | | | | (__| |_| | (_) | | | \__ \ | (_| | __/ | (_| (_) | | | | |_| | | (_) | | __/ +\_| \___/|_| |_|\___|\__|_|\___/|_| |_|___/ \__,_|\___| \___\___/|_| |_|\__|_| \___/|_|\___| + + + + ; "chance" - fait évoluer un paramètre aléatoirement - comme un lancer de dé + ; Paramètres : + ; - Minimum - valeur minimum possible [< Maximum] + ; - Maximum - valeur maximum possible [> Minimum] + chance(1, 5) // Donnera à chaque fois un nombre (décimal) entre 1 et 5 différent + + ; "trajectory" - part d'une valeur et évolue progressivement vers une autre + ; Paramètres : + ; - Départ - valeur de départ + ; - Durée - Durée totale de l'évolution (en nombre de temps) [> 0] + ; - Arrivée - Valeur finale + trajectory(0, 10, 1) // évolue de 0 à 1 progressivement en 10 temps + + ; "oscillation" - va et vient entre deux valeurs + ; Paramètres : + ; - Minimum : valeur minimum [< Maximum] + ; - Maximum : valeur maximum [> Minimum] + ; - Vitesse (par rapport au tempo) [> 0] + oscillation(10, 30, 1/2) // évolue progressivement entre 10 et 30 avec un cycle de 2 temps + + ; "alternate" - alterne entre deux valeurs à une certaine vitesse et avec une proportion + ; Paramètres : + ; - Valeur 1 + ; - Valeur 2 + ; - Vitesse (par rapport au tempo) [> 0] + ; - Proportion de la valeur 1 [0 - 1] - + ; par exemple une proportion de 0.75 fait qu'on restera 3/4 du temps sur la valeur 1, et 1/4 du temps sur la valeur 2 + alternate(50, 55, 1, 0.75) // alterne entre 50 et 55 tous les temps (75% du temps sur valeur 1) + + ; "sequence" - lit une suite de valeurs (utile pour les notes et mélodies) + ; Paramètres : + ; - liste de valeurs + ; - Vitesse (par rapport au tempo) [> 0] + sequence(array(30, 33, 35), 1) + + ; "chaos_control" - génère une valeur qui change en permanence en fonction de l'attracteur + ; chaotique principal + ; Paramètres : + ; - Choix de l'attracteur [1, 2 ou 3] + ; - Valeur minimum + ; - Valeur maximum + chaos_control(1, 0.5, 1) ; Pour du volume + chaos_control(2, 40, 60) ; Pour une note + + + _ + | | + _____ _____ _ __ ___ _ __ | | ___ ___ + / _ \ \/ / _ \ '_ ` _ \| '_ \| |/ _ \/ __| +| __/> < __/ | | | | | |_) | | __/\__ \ + \___/_/\_\___|_| |_| |_| .__/|_|\___||___/ + | | + |_| + + crunchy(beat(4), 0.5, 10, 1) + tempo(0.5) + darkwave(beat(1/2), 0.5, 30, 2) + darkwave(beat(1/2), 0.5, 33, 2) + darkwave(beat(1/4), 0.5, 40, 4) + + chaos_speed(1) + + noisywave(beat(1/4), 0.5, 50, 4) + noisywave(beat(1/4), 0.5, 55, 4) + + brightwave(beat(1/2), 0.7, 70, 1) + buzzwave(beat(1/2), 0.8, 60, 2, 2) + _ _ _ _ _ _ _ + /\| |/\ /\| |/\ /\| |/\ /\| |/\ /\| |/\ /\| |/\ /\| |/\ + \ ` ' / \ ` ' / \ ` ' / \ ` ' / \ ` ' / \ ` ' / \ ` ' / +|_ _|_ _|_ _|_ _|_ _|_ _|_ _| + / , . \ / , . \ / , . \ / , . \ / , . \ / , . \ / , . \ + \/|_|\/ \/|_|\/ \/|_|\/ \/|_|\/ \/|_|\/ \/|_|\/ \/|_|\/ + + /* + crunchy(beat(4), 0.6, 10, 1, 2) + crunchy(beat(8), 0.6, 50, 1, 2) + crunchy(beat(8), 0.6, 55, 1) + */ + ;crunchy(beat(8), 0.8, sequence(array(60, 57, 57, 63, 58), 8), 1) + + tempo(.5) + chaos_speed(7) +/* + brightwave(beat(1/8), 0.5, 50, 3, 3) + brightwave(beat(1/8), 0.5, 53, 3) + brightwave(beat(1/8), 0.5, 57, 3, 3) + */ + + /* + glidy(beat(1/2), 0.7, 60, 2) + glidy(beat(1/3), 0.7, 64, 2) + glidy(beat(1/4), 0.7, 65, 2) + */ + + /* + darkwave(beat(1/4), 0.6, 30, 5, 1) + darkwave(beat(1/4), 0.6, 32, 5, 1) + darkwave(beat(1/4), 0.6, 27, 5) + darkwave(beat(1/4), 0.6, 20, 5) + */ + + _ _ _ _ _ _ _ + /\| |/\ /\| |/\ /\| |/\ /\| |/\ /\| |/\ /\| |/\ /\| |/\ + \ ` ' / \ ` ' / \ ` ' / \ ` ' / \ ` ' / \ ` ' / \ ` ' / +|_ _|_ _|_ _|_ _|_ _|_ _|_ _| + / , . \ / , . \ / , . \ / , . \ / , . \ / , . \ / , . \ + \/|_|\/ \/|_|\/ \/|_|\/ \/|_|\/ \/|_|\/ \/|_|\/ \/|_|\/ + + /* + darkwave(beat(1/4), .7, 60, 4) + darkwave(beat(1/6), .7, 42, 4) + darkwave(beat(1/4), .7, 67, 4) + darkwave(beat(1/6), .7, 10, 4) + + + crunchy(beat(4), 0.9, 10, 1, 2) + crunchy(beat(8), 0.7, 50, 1, 2) + crunchy(beat(8), 0.7, 55, 1) + + crunchy(beat(8), 0.7, sequence(array(60, 57, 57, 63, 58), 8), 1) + + tempo(.125) + chaos_speed(8) + + ping(beat(4), 1, sequence(array(50, 55, 50, 57, 50, 58), 1), 1) + */ + /* + brightwave(beat(1/8), 0.5, 50, 3, 3) + brightwave(beat(1/8), 0.5, 53, 3) + brightwave(beat(1/8), 0.5, 57, 3, 3) + */ +/* + brightwave(beat(1/8), 0.5, 50, 3, 3) + brightwave(beat(1/8), 0.5, 53, 3) + brightwave(beat(1/8), 0.5, 57, 3, 3) + */ + /* + tempo(1) + glidy(beat(1/2), 0.3, 60, 2) + glidy(beat(1/3), 0.3, 64, 3) + glidy(beat(1/4), 0.4, 65, 4) + glidy(beat(1/7), 0.5, 40, 5) + glidy(beat(1/5), 0.4, 44, 6) + glidy(beat(1/6), 0.5, 45, 7) + */ + /* + noisywave(beat(1/4), 0.9, 20, 6) + noisywave(beat(1/4), 0.9, 48, 6) + noisywave(beat(1/8), 0.9, 34, 6) + noisywave(beat(1/8), 0.9, 39, 6) +*/ + + + + + + + + _ _ _ _ _ _ _ + /\| |/\ /\| |/\ /\| |/\ /\| |/\ /\| |/\ /\| |/\ /\| |/\ + \ ` ' / \ ` ' / \ ` ' / \ ` ' / \ ` ' / \ ` ' / \ ` ' / +|_ _|_ _|_ _|_ _|_ _|_ _|_ _| + / , . \ / , . \ / , . \ / , . \ / , . \ / , . \ / , . \ + \/|_|\/ \/|_|\/ \/|_|\/ \/|_|\/ \/|_|\/ \/|_|\/ \/|_|\/ + + + + tempo(1) + chaos_speed(1) + darkwave(beat(1/4), .5, 60, 4) + darkwave(beat(1/6), .65, 36, 6) + darkwave(beat(1/8), .65, 38, 8) + darkwave(beat(1/4), .65, 67, 4) + darkwave(beat(1/6), .65, 24, 6) + darkwave(beat(1/3), .65, 40, 4) + darkwave(beat(1/5), .65, 31, 5) + + crunchy(beat(4), 1, 12, 1/2, 2) + crunchy(beat(1), 1, 22, 1) + crunchy(beat(8), 0.6, 50, 1/2, 2) + crunchy(beat(8), 0.6, 55, 1/2) + + crunchy(beat(8), 0.7, sequence(array(60, 57, 57, 63, 58), 8), 1/2) + crunchy(beat(8), 0.8, sequence(array(72, 48, 74, 75, 46, 50, 78), 8), 1/2) + + + /* + tempo(.125) + chaos_speed(8) + + ping(beat(4), 1, sequence(array(50, 55, 50, 57, 50, 58), 1), 1) + */ + /* + brightwave(beat(1/8), 0.5, 50, 3, 3) + brightwave(beat(1/8), 0.5, 53, 3) + brightwave(beat(1/8), 0.5, 57, 3, 3) + */ +/* + brightwave(beat(1/8), 0.5, 50, 3, 3) + brightwave(beat(1/8), 0.5, 53, 3) + brightwave(beat(1/8), 0.5, 57, 3, 3) + */ + /* + tempo(1) + glidy(beat(1/2), 0.3, 60, 2) + glidy(beat(1/3), 0.3, 64, 3) + glidy(beat(1/4), 0.4, 65, 4) + glidy(beat(1/7), 0.5, 40, 5) + glidy(beat(1/5), 0.4, 44, 6) + glidy(beat(1/6), 0.5, 45, 7) + */ + /* + noisywave(beat(1/4), 0.9, 20, 6) + noisywave(beat(1/4), 0.9, 48, 6) + noisywave(beat(1/8), 0.9, 34, 6) + noisywave(beat(1/8), 0.9, 39, 6) +*/ + + + +/* +Exemple réalisé en première séance à l'école du Centre + crunchy(beat(2), 0.9, chance(15, 80), 0.5) + buzzy(beat(1/4), 0.8, 10, 4) + tempo(4) + glidy(beat(1), 0.6, 60, 1) + darkwave(beat(1/4), 0.9, 30, 4) +*/ + + + + +/* + + ping(beat(8), 1, 10, 1/8) + ping(beat(8), 1, sequence(array(20, 22), 1/4), 1/8) + crunchy(beat(4), 0.9, chance(30, 80), 1/6) + crunchy(beat(4), 0.8, chance(30, 80), 1/6) + kick(beat(2), 0.5) + snare(beat(1), 0.8, 100, 1/10) + +*/ + +/* + noisywave(beat(1/4), 0.6, 80, 4) + noisywave(beat(1/8), 0.6, 60, 8) + noisywave(beat(1/6), 0.6, 40, 6) + snare(rhythm(array(1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1), 8), 0.5) + kick(rhythm(array(0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0), 8), 0.7) +*/ + +/* + ; MELODY + ping(beat(2), oscillation(.5, 1, 1/2), sequence(array(52, 55, 50, 48, 50)+sequence(array(2, 0), 2), 2), oscillation(.5, 2, 1/2)) + + ; CHORD + buzzwave(beat(4), oscillation(0, .5, 1/6), sequence(array(52, 55, 50, 48, 50)-24, 1/2), 1) + buzzwave(beat(4), oscillation(0, .5, 1/3), sequence(array(52, 55, 50, 48, 50)-7, 1/2), 2) + + ; BUZZ + noisywave(beat(1/4), .5, sequence(array(52, 55), 2), 8) +*/ + + + + _ _ _ _ _ _ _ + /\| |/\ /\| |/\ /\| |/\ /\| |/\ /\| |/\ /\| |/\ /\| |/\ + \ ` ' / \ ` ' / \ ` ' / \ ` ' / \ ` ' / \ ` ' / \ ` ' / +|_ _|_ _|_ _|_ _|_ _|_ _|_ _| + / , . \ / , . \ / , . \ / , . \ / , . \ / , . \ / , . \ + \/|_|\/ \/|_|\/ \/|_|\/ \/|_|\/ \/|_|\/ \/|_|\/ \/|_|\/ + + ; Rythme 1 + kick(swing(1, 0.75), 0.5) + snare(swing(0.5, 0.25), 0.7) + hihat( rhythm(array(1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1), 4), 0.5, 95, 0.1) + + + + +; Ping à corriger +; Noisywave avec mode sauvage ou son grave avec mode sauvage aussi diff --git a/public/system-files/archives.orc b/public/system-files/archives.orc new file mode 100644 index 0000000..6fc82fb --- /dev/null +++ b/public/system-files/archives.orc @@ -0,0 +1,162 @@ + + kick(swing(1, 0.75), 0.5) + snare(swing(0.5, 0.25), 0.7) + hihat( rhythm(array(1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1), 4), 0.3, 95, 0.05) + + tempo(0.5) + chaos_speed(0.5) + ; Base rythmique + crunchy(beat(8), 1, 8, 1/4) + crunchy(beat(2), 1, 20, 1/2) + crunchy(beat(8), 1, trajectory(60, 15, 30), 1/4) + crunchy(beat(4), 1, 23, 1/4) + + ; Mélodie aléatoire + buzzy(beat(8), 0.8, chance(60, 90), 1/16) + buzzy(beat(8), 0.8, chance(70, 80), 1/16) + buzzy(beat(4), 0.8, trajectory(80, 10, 60), 1/8) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ping(beat(1), 0.8, 50,1) + ping(beat(1/2), 0.2, 90,2) + ping(beat(4), 0.9, 41, 1/3) + ping(beat(4), 0.9, 46, 1/4) + ping(beat(1/2), 0.8, 37,3) + ping(beat(1/4), 0.8, 60,4) + ping(beat(1/2), 0.8, 62,2) + ping(beat(1/6), 0.8, 67,6) + + buzzwave(beat(1/3), 0.8, sequence(array(20, 25), 1), 3) + + tempo(1) + snare(swing(2, 0.25), 0.9) + kick(swing(0.5, 0.75), 0.5) + hihat( swing(4, 0.25), 0.6, chance(80, 100), 0.1) + hihat( beat(8), 0.8, 100, 0.05) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Lunégroove + noisywave(beat(1/4), 0.9, sequence(array(20, 30, 23, 27), 1/4), 4) + noisywave(beat(1/8), 0.9, sequence(array(60, 60, 65, 53), 1/8), 8) + noisywave(beat(1/6), 0.9, sequence(array(40, 43, 46), 1/6), 6) + glidy(rhythm(array(1, 1, 1, 0, 0, 1), 2), 0.5, sequence(array(70, 75, 53, 51), 3), 1/4) + glidy(rhythm(array(0, 1, 0, 1, 1, 1), 2), 0.5, sequence(array(73, 65, 60), 3), 1/2) + brightwave(beat(1/4), 1, sequence(array(15, 18, 23), 1/4), 7) + + snare(rhythm(array(1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1), 4), 0.5) + kick(rhythm(array(0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0), 2), 0.7) + tempo(1) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ; LA VOITURE EN PANNE !!!!!! + + tempo(1.5) + kick(rhythm(array(1, 0, 0, 0, 0, 1, 0, 0), 2), 0.45) + snare(rhythm(array(0, 0, 1, 0, 0, 0, 1, 0), 2), 0.25) + hihat(beat(4), 0.15, 95, 1/12) + + ping(beat(2), 0.45, sequence(array(20, 21), 1/8), .5) + ping(beat(1/4), .95, sequence(array(20, 21)-12, 1/8), 4) + + crunchy(beat(1), .95, sequence(array(20, 18, 28, 21), 1/7)+12*sequence(array(1, 2, 3), 1), 1+sequence(array(1, 2, 3), 1)) + + brightwave(swing(1/8, .95), 0.55, sequence(array(21, 20, 25, 30)+24, 1/8), 6) + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ; on va faire fortune dans le reggae + + tempo(1) + crunchy(rhythm(array(1, 1, 1, 1, 1, 1, 0, 0), 8), 0.8, sequence(array(11, 17), 1/2), 1/4) + + ping(rhythm(array(0, 1), 2), 0.9, sequence(array(43, 41), 1/2), 0.25 ) + ping(rhythm(array(0, 1), 2), 0.9, sequence(array(47, 45), 1/2), 0.25) + ping(rhythm(array(0, 1), 2), 0.9, sequence(array(50, 48), 1/2), 0.25) + + glidy(beat(1/4), 0.2, sequence(array(49, 55, 52, 67, 46, 70), 1/4), 6) + + kick(rhythm(array(1, 0), 2), 0.8) + snare(beat(1), 0.6) + hihat(rhythm(array(1, 1, 1, 0, 1, 0), 6), 0.7, 90, 0.1) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ; Ardoise + tempo(1.25) + kick(rhythm(array(1, 1, 0, 0, 0, 1), 4), .5) + snare(beat(1/2), .5) + hihat(rhythm(array(0, 1, 1, 0, 1, 1, 1), 4), .25, 95-4*swing(1, .25), 1/8) + + ping(beat(6), .35, sequence(array(48, 51, 55, 60, 48, 51, 55, 60, 47, 51, 55, 60, 44, 51, 55, 60), oscillation(6, 12, 1/32)), oscillation(.25, 3, 1/8)) + ping(beat(6), .35, sequence(array(48, 51, 55, 60, 48, 51, 55, 60, 47, 51, 55, 60, 44, 51, 55, 60), oscillation(2, 16, 1/24)), oscillation(.25, 3, 1/3)) + darkwave(beat(3), .85, sequence(array(48, 51, 55, 60, 47, 51, 55, 60, 44, 51, 55, 60)-24, oscillation(3, 9, 1/48)), oscillation(1, 3, 1/3)) + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;KS + chaos_speed(1) + $tempo = .5 + ;ks(beat(1), 0.8, 44, .5) + ks(beat(.25), 0.8, 16, .5) + ks(beat(1), 0.9, 52, .5) + ks(beat(3), 0.9, 64, 1) + ks(beat(4), 0.9, 83, 2) + ;ks(beat(8), 0.5, sequence(array(75, 83, 80, 85, 83), 1), .25) + ;ks(beat( alternate(.5, 2, .5, 0.5) ), 0.8, 30, 1) + + ;ping(beat(4), 0.8, 72, .8) + ;crunchy(beat(16), 0.6, trajectory(16, 4, 32), .5) + + + + + + + + + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;MOBILE 1 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + $tempo = 0.25 + klf = abs(lfo:k(1, 0.05)) + kilf = 1 - klf + strike(beat(1/16), 0.9 * klf , alternate(40, 28, 1/4, 0.5), 16) + strike(beat(1/16), 0.9 * klf , alternate(6, 18, 1/4, 0.5), 16) + strike(beat(1/12), 0.9 * kilf , alternate(30, 18, 1/8, 0.5), 12) + + kmet = drunk(0.125, 0.25) + ktog init 0 + krhythm_mult init 1 + if(kmet == 1 ) then + ktog = 1 - ktog + if(rint:k(1, 6) == 1) then + krhythm_mult = rint:k(1, 3) + endif + endif + + if(ktog == 1) then + strike( rhythm(array(1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1), 4 * krhythm_mult), + oscillation(0.1, 0.6, 10), + sequence(array(60, 64, 66, 67, 69), 8) + + sequence(array(2, 3, 8, 1, 5, 10), 3.3), 1/2) + else + buzzy(beat(krhythm_mult * 2), 0.9, oscillation(40, 70, 10), 1) + buzzy(beat(1/2), 0.8 * klf , alternate(44, 33, 1/4, 0.5), 2) + buzzy(beat(1/3), 0.8 * kilf , alternate(24, 21, 1/8, 0.5), 2) + endif + + smooth(drunk(1/10, 0.3), 0.5, sequence(array(60, 30, 64, 67), 1), 8) + smooth(drunk(1/9, 0.3), 0.5, sequence(array(48, 18, 66, 70, 65), 1/2), 8) \ No newline at end of file diff --git a/public/system-files/examples.orc b/public/system-files/examples.orc new file mode 100644 index 0000000..0cfa6be --- /dev/null +++ b/public/system-files/examples.orc @@ -0,0 +1,9 @@ +░▒▓████████▓▒░▒▓█▓▒░░▒▓█▓▒░░▒▓██████▓▒░░▒▓██████████████▓▒░░▒▓███████▓▒░░▒▓█▓▒░ ░▒▓████████▓▒░░▒▓███████▓▒░ +░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ +░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ +░▒▓██████▓▒░ ░▒▓██████▓▒░░▒▓████████▓▒░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓███████▓▒░░▒▓█▓▒░ ░▒▓██████▓▒░ ░▒▓██████▓▒░ +░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ +░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ +░▒▓████████▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓████████▓▒░▒▓████████▓▒░▒▓███████▓▒░ + + \ No newline at end of file diff --git a/public/system-files/globals.orc b/public/system-files/globals.orc new file mode 100644 index 0000000..6d70c2d --- /dev/null +++ b/public/system-files/globals.orc @@ -0,0 +1,159 @@ +gktempo init 1 +gkphasor init 0 +gkslowph init 0 +gkbeat init 0 +gkgain init 0.05 +#define tempo #gktempo# + +instr metronome_instr + kenv = expseg(1.01, p3, 0.01) - 0.01 + asig = oscili(0.3, 3000) * kenv + chnmix(asig, "out1") + chnmix(asig, "out2") +endin + +opcode metronome, 0, 0 + kmet = gkbeat + if(kmet == 1) then + schedulek("metronome_instr", 0, 0.1) + endif +endop + +instr gclock + gkphasor = phasor:k(gktempo) + gkslowph = phasor:k(gktempo / 100) + gkbeat = metro:k(gktempo) +endin +schedule("gclock", 0, -1) + +opcode reset_clock, 0, 0 + turnoff2( nstrnum("gclock"), 0, 0) + schedule("gclock", 0.01, -1) +endop + +instr audio_out + + a1 = clip:a(chnget:a("out1"), 0, 0.99);limit:a(chnget:a("out1"), -1, 1) ;* gkgain + a2 = clip:a(chnget:a("out2"), 0, 0.99);limit:a(chnget:a("out2"), -1, 1) ;* gkgain + chnclear("out1") + chnclear("out2") + outs a1, a2 +endin +schedule("audio_out", 0, -1) + + +gklorenz_speed init 0.00001 + +gklorenz_x init 0 +gklorenz_y init 0 +gklorenz_z init 0 +gkmirlorenz_x init 0 +gkmirlorenz_y init 0 +gkmirlorenz_z init 0 +galorenz_x init 0 +galorenz_y init 0 +galorenz_z init 0 + +opcode lorenz_ctl, aaa, kppp + kspeed, ix, iy, iz xin + kspeed = limit:k(kspeed, 0.0000001, 0.09) + ksv init 10 + krv init 28 + kbv init 2.6 +; kh init 0.001 + kh = kspeed + ix = (ix != 0) ? ix : 0.6 + iy = (iy != 0) ? iy : 0.6 + iz = (iz != 0) ? iz : 0.6 + iskip = 1 + ax1, ay1, az1 lorenz ksv, krv, kbv, kh, ix, iy, iz, iskip + + ax1 /= krv + ay1 /= krv + az1 /= krv + + xout ax1, ay1, az1 +endop + +#define LORENZ_TIME #720# + +instr lorenz_sched + ix = random:i(0, 1) + iy = random:i(0, 1) + iz = random:i(0, 1) + ax, ay, az lorenz_ctl gklorenz_speed, ix, iy, iz + gklorenz_x = lag(k(ax), 0.01) + gklorenz_y = lag(k(ay), 0.01) + gklorenz_z = lag(k(az), 0.01) + gkmirlorenz_x = mirror:k(gklorenz_x, 0, 1) + gkmirlorenz_y = mirror:k(gklorenz_y, 0, 1) + gkmirlorenz_z = mirror:k(gklorenz_z, 0, 1) + galorenz_x = ax + galorenz_y = ay + galorenz_z = az + schedule("lorenz_sched", $LORENZ_TIME, $LORENZ_TIME) +endin + +schedule("lorenz_sched", 0, $LORENZ_TIME) + +#define FQ # + isavage_mode init p6 + ifq mtof inote + + ksavage init 0 + if(isavage_mode == 1) then + ksavage = savage_1() + elseif(isavage_mode == 2) then + ksavage = savage_2() + elseif(isavage_mode == 3) then + ksavage = savage_3() + elseif(isavage_mode == 4) then + ksavage = savage_4() + elseif(isavage_mode == 5) then + ksavage = savage_5() + endif + + kfq = limit:k(ifq + (ksavage * ifq), 20, 20000) +# + +#define FQPERC # + isavage_mode = rint:i(1, 5) + ksavage init 0 + if(isavage_mode == 1) then + ksavage = savage_1() + elseif(isavage_mode == 2) then + ksavage = savage_2() + elseif(isavage_mode == 3) then + ksavage = savage_3() + elseif(isavage_mode == 4) then + ksavage = savage_4() + elseif(isavage_mode == 5) then + ksavage = savage_5() + endif +# + +#define SAVAGE(isavage_mode') # + ksavage init 0 + if(isavage_mode == 1) then + ksavage = savage_1() + elseif(isavage_mode == 2) then + ksavage = savage_2() + elseif(isavage_mode == 3) then + ksavage = savage_3() + elseif(isavage_mode == 4) then + ksavage = savage_4() + elseif(isavage_mode == 5) then + ksavage = savage_5() + endif +# + +opcode tempo, 0, k + kval xin + $tempo = limit:k(kval, 0.001, 10) +endop + +opcode chaos_speed, 0, k + kval xin + gklorenz_speed = 0.0000001 * (limit:k(kval, 0.01, 100) * 10) +endop + diff --git a/public/system-files/lib.orc b/public/system-files/lib.orc new file mode 100644 index 0000000..4a979db --- /dev/null +++ b/public/system-files/lib.orc @@ -0,0 +1,414 @@ +; instruments management +instr KillImpl + Sinstr = p4 + if (nstrnum(Sinstr) > 0) then + turnoff2(Sinstr, 0, 0) + endif + turnoff +endin + +opcode kill, 0, S + Sinstr xin + schedule("KillImpl", 0, .05, Sinstr) +endop + +opcode refresh, 0, S + Sinstr xin + if (nstrnum(Sinstr) > 0) then + kill(Sinstr) + schedule(Sinstr, ksmps / sr, -1) + endif +endop + +#define _endin # +endin +refresh("livecoding") +# + +opcode rint, i, ii + imin, imax xin + irnd = int(random:i(imin, int(imax) + 0.99)) + xout irnd +endop + +opcode rint, k, kk + kmin, kmax xin + krnd = int(random:k(kmin, int(kmax) + 0.99)) + xout krnd +endop + +opcode euclidian, k, kkkk + konset, kdiv, kpulses, krot xin + kphasor = gkphasor + kph = int( ( ( (kphasor + krot) * kdiv) / 1) * kpulses) + keucval = int((konset / kpulses) * kph) + kold_euc init i(keucval) + kold_ph init i(kph) + kres = ((kold_euc != keucval) && (kold_ph != kph)) ? 1 : 0 + kold_euc = keucval + kold_ph = kph + xout kres +endop + +// Simple rhythm array +opcode array_rhythm, k, k[] + karr[] xin + kmet = gkbeat + kcnt init 0 + ilen = lenarray(karr) + ktrig = 0 + if( kmet > 0 ) then + kval = karr[kcnt] + ktrig = kval + kcnt = (kcnt + 1) % ilen + endif + + xout ktrig +endop + +opcode trunc, k, k + kx xin + ky = (kx < 0) ? -(floor(-kx)) : floor(kx) + xout ky +endop +opcode mfmod, k, kk + kone, ktwo xin + kres = kone - trunc(kone/ktwo) * ktwo + xout kres +endop + +opcode phase_trig, k, k + kphase xin + kold init 1 + ktrigger init 0 + ktrigger = 0 + if(kold > kphase) then + ktrigger = 1 + endif + kold = kphase + xout ktrigger +endop + +opcode downprint, 0, kki + kprint, kdown, imargin xin + kcnt init 0 + if(kcnt == 0) then + printk2(kprint, imargin) + endif + kcnt = (kcnt + 1) % kdown +endop + + +opcode phasor_fqmult, k, kk + kphase, kmult xin + if(kmult <= 0) then + kmult = 1 + endif + kfphase init 0 + if(kmult <= 1) then + kfphase = mfmod(gkslowph * (kmult * 100), 1) + elseif(kmult > 1) then + kfphase = mfmod(gkslowph * (kmult * 100), 1) + ;kfphase= mfmod(gkphasor * kmult, 1) + endif + xout kfphase +endop + +opcode rh_subd, k, k[]P + karr[], kpdiv xin + ilen = lenarray(karr) + if(kpdiv <= 0) then + kpdiv = ilen + endif + kdiv = kpdiv + kph = phasor_fqmult(gkphasor, kdiv ) + ktr = phase_trig(kph) + kidx init 0 + ktrigger = 0 + if(ktr == 1) then + ktrigger = karr[kidx] + kidx = (kidx+1) % ilen + endif + xout ktrigger +endop + +opcode rhythm, k, k[]P + karr[], kpdiv xin + ilen = lenarray(karr) + if(kpdiv <= 0) then + kpdiv = ilen + endif + kdiv = kpdiv + kph = phasor_fqmult(gkphasor, kdiv) + ktr = phase_trig(kph) + kidx init 0 + ktrigger = 0 + if(ktr == 1) then + ktrigger = karr[kidx] + kidx = (kidx+1) % ilen + endif + xout ktrigger +endop + +opcode beat, k, k + kmult xin + kmult = limit:k(kmult, 0.001, 1000) + km = rhythm(array(1), kmult) + ;kph = phasor:k(kmult * gktempo) + ;km = phase_trig(kph) + xout km +endop + +opcode swing, k, kk + kspeed, ksw xin + ktrig init 0 + kold init 0 + kph = phasor_fqmult(gkphasor, kspeed) + ktr1 = phase_trig(kph) + ktr2 = 0 + if(kold < ksw && kph >= ksw) then + ktr2 = 0.6 + endif + kold = kph + ktrig = limit:k(ktr1 + ktr2, 0, 1) + xout ktrig +endop + +opcode drunk, k, kk + kspeed, kdrunk xin + kold init 0 + kspeed = limit:k(kspeed, 0.001, 1000) + knoi = random:k(0, kdrunk) + + kph = phasor_fqmult(gkphasor, kspeed) + ;kph = phase_incr_mod(gkphasor, 1/ kspeed) + ktrig = limit:k(phase_trig(kph) + ((kold < knoi && kph > knoi) ? 1 : 0), 0, 1) + + kold = kph + xout ktrig +endop + + +opcode filter_first_trig, k, k + ktrig xin + ksub init 1 + ktrig -= ksub + ksub = 0 + xout ktrig +endop + +opcode check_trig, k, k + ktrig xin + kf = filter_first_trig(ktrig) + kres = (changed:k(kf) > 0 && kf > 0) ? 1 : 0 + xout kres +endop + +opcode to_tempo_dur, k, k + kdur xin + ktdur = kdur / gktempo + xout ktdur +endop + +opcode chance, k, kk + kmin, kmax xin + kres = random:k(kmin, kmax) + xout kres +endop + +opcode alternate, k, kkkO + kone, ktwo, kspeed, kduty xin + + if(kduty == 0) then + kduty = 0.5 + endif + if(kspeed <= 0) then + kspeed = 1 + endif + kspeed = limit:k(to_tempo_dur(kspeed), 0.0001, 1000) + kph = phasor_fqmult(gkphasor, kspeed) + kres init i(kone) + if(kph < kduty) then + kres = kone + else + kres = ktwo + endif + xout kres +endop + +opcode trajectory, k, kkk + kfrom, kdur, kto xin + kdur = to_tempo_dur(kdur) + kres = linseg:k( i(kfrom), i(kdur), i(kto) ) + xout kres +endop + +opcode oscillation, k, kkk + kmin, kmax, kspeed xin + if(kmin > kmax) then + ktmp = kmin + kmin = kmax + kmax = ktmp + endif + kspeed = limit:k(gktempo / kspeed, 0.001, 1000) + + kfq = kspeed ; / gktempo + iphs init i(gkphasor) + kosc = oscili(0.5, kfq, -1, iphs) + 0.5 + kres = kosc * (kmax - kmin) + kmin + xout kres +endop + +opcode sequence, k, k[]P + karr[], kpdiv xin + ilen = lenarray(karr) + if(kpdiv <= 0) then + kpdiv = ilen + endif + kdiv = kpdiv + kph = phasor_fqmult(gkphasor, kdiv ) + ktr = phase_trig(kph) + kidx init 0 + if(ktr == 1) then + kidx = (kidx+1) % ilen + endif + ktrigger = karr[kidx] + xout ktrigger +endop + +opcode random_array_int, i[], iii + isize, imin, imax xin + iarr[] init isize + + icnt init 0 + while icnt < isize do + iarr[icnt] = int(random:i(imin, imax+0.99)) + icnt += 1 + od + xout iarr +endop + +opcode random_array_int, k[], iii + isize, imin, imax xin + kArr[] init isize + icnt init 0 + iArr[] = random_array_int(isize, imin, imax) + while icnt < isize do + kArr[icnt] = iArr[icnt] + icnt += 1 + od + xout kArr +endop + +#define arr #fillarray(# + +; kind of random based savage +opcode savage_1, k, 0 + k1 = gkmirlorenz_x + k2 = abs(oscili:k(1, gkmirlorenz_y * 6 + 0.01)) + k3 = rspline:k(0, gkmirlorenz_z, 1, 5) + + kfq = gkmirlorenz_x * rspline:k(1, 3, 1, 5) + 0.001 + kmod = beat(kfq) ;lfo:k(1, kfq, 3) + + ksel init 0 + ksel_sig init 0 + if(trigger:k(kmod, 0.5, 0) == 1) then + ksel = rint:k(0, 2) + endif + + if(ksel == 0) then + ksel_sig = k1 + elseif(ksel == 1) then + ksel_sig = k2 + elseif(ksel == 2) then + ksel_sig = k3 + endif + + kres = (kmod > 0) ? ksel_sig : 0 + xout kres +endop + +; direct lorenz savage mode +opcode savage_2, k, 0 + kmult = pow(abs(lfo:k(1, rspline:k(0, gklorenz_x, 0.1, 3), 1)),3) + kmod = abs(poscil:k( pow(gkmirlorenz_z, 3) * kmult, gkmirlorenz_x * gkmirlorenz_y * 40)) + xout kmod +endop + +; pwm based savage mode +opcode savage_3, k, 0 + kduty = gkmirlorenz_y + kph = phasor:k(gkmirlorenz_x * 5 ) + kres init 0 + if(kph < kduty) then + kres = 1 + else + kres = 0 + endif + + kmult = mirror:k(gklorenz_z, 0, 1) + kres *= kmult + xout kres +endop + +opcode savage_4, k, 0 + iarr[] = fillarray(random:i(0, 1), random:i(0, 1), random:i(0, 1), random:i(0, 1)) + + kchange init 1 + kchange = changed:k(round(abs(gklorenz_x ))) + kmod init 1 + if(kchange == 1) then + iarr[0] = random:i(0, 1) + iarr[1] = random:i(0, 1) + iarr[2] = random:i(0, 1) + iarr[3] = random:i(0, 1) + endif + ksel = int(abs(gklorenz_x) * 4.99) % 4 + kres = limit:k (lineto(iarr[ksel], mirror:k(abs(gklorenz_y), 0, 1) ), 0, 1) + xout kres +endop + +opcode savage_5, k, 0 + kamp = pow(abs(gklorenz_x), 2) + kfq = 8 - (0.01 + abs(gklorenz_y) * 8) + kmod = oscili:k(kamp, kfq) + xout kmod +endop + +opcode chaos_rhythm, k, kk + kchoice, kthresh xin + kthresh = limit:k(kthresh, 0, 1) + kchoice = int(limit:k(kchoice, 1, 3.99)) + kval init 0 + if(kchoice == 1) then + kval = gkmirlorenz_x + elseif(kchoice == 2) then + kval = gkmirlorenz_y + elseif(kchoice == 3) then + kval = gkmirlorenz_z + endif + + kres = trigger:k(kval, kthresh, 2) + xout kres +endop + +opcode chaos_control, k, kkk + kchoice, kmin, kmax xin + kmax = max:k(kmin, kmax) + kmin = min:k(kmin, kmax) + kchoice = int(limit:k(kchoice, 1, 3.99)) + kval init 0 + if(kchoice == 1) then + kval = gkmirlorenz_x + elseif(kchoice == 2) then + kval = gkmirlorenz_y + elseif(kchoice == 3) then + kval = gkmirlorenz_z + endif + kdiff = kmax - kmin + kres = kval * kdiff + kmin + xout kres +endop + + diff --git a/public/system-files/livecode.orc b/public/system-files/livecode.orc new file mode 100644 index 0000000..cc45500 --- /dev/null +++ b/public/system-files/livecode.orc @@ -0,0 +1,3 @@ +instr livecoding + +$_endin diff --git a/public/system-files/scale.orc b/public/system-files/scale.orc new file mode 100644 index 0000000..1083907 --- /dev/null +++ b/public/system-files/scale.orc @@ -0,0 +1,207 @@ +gi3semitone ftgen 0, 0, 5, -2, 0, 3, 6, 9, 12 +gi4semitone ftgen 0, 0, 4, -2, 0, 4, 8, 12 +giaeolian ftgen 0, 0, 7, -2, 0, 2, 3, 5, 7, 8, 10 +giaeolian ftgen 0, 0, 8, -2, 0, 2, 3, 5, 7, 8, 10, 12 +gialgerian ftgen 0, 0, 8, -2, 0, 2, 3, 6, 7, 8, 11, 12 +gialgerian1 ftgen 0, 0, 8, -2, 0, 2, 3, 6, 7, 8, 11, 12 +gialgerian2 ftgen 0, 0, 8, -2, 0, 2, 3, 5, 7, 8, 10, 12 +gialtered ftgen 0, 0, 8, -2, 0, 1, 3, 4, 6, 8, 10, 12 +giarabian ftgen 0, 0, 8, -2, 0, 1, 4, 5, 7, 8, 11, 12 +giaugmented ftgen 0, 0, 7, -2, 0, 3, 4, 7, 8, 11, 12 +gibalinese ftgen 0, 0, 6, -2, 0, 1, 3, 7, 8, 12 +gibebopdominant ftgen 0, 0, 9, -2, 0, 2, 4, 5, 7, 9, 10, 11, 12 +gibebopdominantflatnine ftgen 0, 0, 9, -2, 0, 1, 4, 5, 7, 9, 10, 11, 12 +gibebopmajor ftgen 0, 0, 9, -2, 0, 2, 4, 5, 7, 8, 9, 11, 12 +gibebopminor ftgen 0, 0, 9, -2, 0, 2, 3, 5, 7, 8, 9, 10, 12 +gibeboptonicminor ftgen 0, 0, 9, -2, 0, 2, 3, 5, 7, 8, 9, 11, 12 +giblues ftgen 0, 0, 7, -2, 0, 3, 5, 6, 7, 10, 12 +gibyzantine ftgen 0, 0, 8, -2, 0, 1, 4, 5, 7, 8, 11, 12 +gichahargah ftgen 0, 0, 8, -2, 0, 1, 4, 5, 7, 8, 11, 12 +gichinese ftgen 0, 0, 6, -2, 0, 2, 4, 7, 9, 12 +gichinese2 ftgen 0, 0, 6, -2, 0, 4, 6, 7, 11, 12 +gichroma ftgen 0, 0, 12, -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 +gichromatic ftgen 0, 0, 13, -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 +gidim ftgen 0, 0, 4, -2, 0, 3, 6, 9 +gidiminished ftgen 0, 0, 9, -2, 0, 2, 3, 5, 6, 8, 9, 11, 12 +gidorian ftgen 0, 0, 7, -2, 0, 2, 3, 5, 7, 9, 10 +gidorian ftgen 0, 0, 8, -2, 0, 2, 3, 5, 7, 9, 10, 12 +gidoubleharmonic ftgen 0, 0, 8, -2, 0, 1, 4, 5, 7, 8, 11, 12 +giegyptian ftgen 0, 0, 8, -2, 0, 2, 3, 6, 7, 8, 11, 12 +gienigmatic ftgen 0, 0, 8, -2, 0, 1, 4, 6, 8, 10, 11, 12 +giethiopian ftgen 0, 0, 8, -2, 0, 2, 4, 5, 7, 8, 11, 12 +giflamenco ftgen 0, 0, 9, -2, 0, 1, 3, 4, 5, 7, 8, 10, 12 +gigypsy ftgen 0, 0, 8, -2, 0, 1, 4, 5, 7, 8, 11, 12 +giharmonic ftgen 0, 0, 7, -2, 0, 2, 4, 5, 7, 9, 11 +giharmonicmajor ftgen 0, 0, 8, -2, 0, 2, 4, 5, 8, 9, 11, 12 +giharmonicminor ftgen 0, 0, 7, -2, 0, 2, 3, 5, 7, 8, 11 +giharmonicminor ftgen 0, 0, 8, -2, 0, 2, 3, 5, 7, 8, 11, 12 +gihindu ftgen 0, 0, 8, -2, 0, 2, 4, 5, 7, 8, 10, 12 +gihirajoshi ftgen 0, 0, 6, -2, 0, 2, 3, 7, 8, 12 +gihungariangypsy ftgen 0, 0, 8, -2, 0, 2, 3, 6, 7, 8, 11, 12 +gihungarianmajor ftgen 0, 0, 8, -2, 0, 3, 4, 6, 7, 9, 10, 12 +gihungarianminor ftgen 0, 0, 8, -2, 0, 2, 3, 6, 7, 8, 11, 12 +giindian ftgen 0, 0, 8, -2, 0, 1, 3, 4, 7, 8, 10, 12 +giinverteddiminished ftgen 0, 0, 9, -2, 0, 1, 3, 4, 6, 7, 9, 10, 12 +giionian ftgen 0, 0, 7, -2, 0, 2, 4, 5, 7, 9, 11 +giionian ftgen 0, 0, 8, -2, 0, 2, 4, 5, 7, 9, 11, 12 +giiwato ftgen 0, 0, 6, -2, 0, 1, 5, 6, 10, 12 +gijapanese ftgen 0, 0, 6, -2, 0, 1, 5, 7, 8, 12 +gijavanese ftgen 0, 0, 8, -2, 0, 1, 3, 5, 7, 9, 10, 12 +gijewish ftgen 0, 0, 8, -2, 0, 1, 4, 5, 7, 8, 10, 12 +gikumoi ftgen 0, 0, 6, -2, 0, 1, 5, 7, 8, 12 +gileadingwholetone ftgen 0, 0, 8, -2, 0, 2, 4, 6, 8, 10, 11, 12 +gilocrian ftgen 0, 0, 7, -2, 0, 1, 3, 5, 6, 8, 10 +gilocrian ftgen 0, 0, 8, -2, 0, 1, 3, 5, 6, 8, 10, 12 +gilocrianmajor ftgen 0, 0, 8, -2, 0, 2, 4, 5, 6, 8, 10, 12 +gilocriannatural ftgen 0, 0, 8, -2, 0, 2, 3, 5, 6, 8, 10, 12 +gilocriansuper ftgen 0, 0, 8, -2, 0, 1, 3, 4, 6, 8, 10, 12 +gilocrianultra ftgen 0, 0, 8, -2, 0, 1, 3, 4, 6, 8, 9, 12 +gilydian ftgen 0, 0, 7, -2, 0, 2, 4, 6, 7, 9, 11 +gilydian ftgen 0, 0, 8, -2, 0, 2, 4, 6, 7, 9, 11, 12 +gilydianaugmented ftgen 0, 0, 8, -2, 0, 2, 4, 6, 8, 9, 10, 12 +gilydiandominant ftgen 0, 0, 8, -2, 0, 2, 4, 6, 7, 9, 10, 12 +gilydianminor ftgen 0, 0, 8, -2, 0, 2, 4, 6, 7, 8, 10, 12 +gim7 ftgen 0, 0, 19, -2, 0, 3, 7, 10, 14, 17, 21, 24, 27, 31, 34, 38, 41, 45, 48, 51, 55, 58, 62 +gimajor ftgen 0, 0, 7, -2, 0, 2, 4, 5, 7, 9, 11 +gimarva ftgen 0, 0, 8, -2, 0, 1, 4, 6, 7, 9, 11, 12 +gimelodicminor ftgen 0, 0, 8, -2, 0, 2, 3, 5, 7, 9, 11, 12 +gimelodicminorascending ftgen 0, 0, 7, -2, 0, 2, 3, 5, 7, 9, 11 +gimelodicminordescending ftgen 0, 0, 7, -2, 0, 2, 3, 5, 7, 8, 10 +giminor ftgen 0, 0, 7, -2, 0, 2, 3, 5, 7, 8, 10 +giminor3 ftgen 0, 0, 7, -2, 0, 2, 2.75, 5, 7.15, 8, 10 +gimjnor ftgen 0, 0, 7, -2, 0, 1.75, 2.75, 5.35, 7.35, 8, 10 +giminor2v5 ftgen 0, 0, 7, -2, 0, 2, 2.5, 5, 7, 8, 10 +gimixolydian ftgen 0, 0, 7, -2, 0, 2, 4, 5, 7, 9, 10 +gimixolydian ftgen 0, 0, 8, -2, 0, 2, 4, 5, 7, 9, 10, 12 +gimixolydianaugmented ftgen 0, 0, 8, -2, 0, 2, 4, 5, 8, 9, 10, 12 +gimohammedan ftgen 0, 0, 8, -2, 0, 2, 3, 5, 7, 8, 11, 12 +gimongolian ftgen 0, 0, 6, -2, 0, 2, 4, 7, 9, 12 +gimonotone ftgen 0, 0, 1, -2, 0 +ginaturalminor ftgen 0, 0, 7, -2, 9, 11, 0, 2, 4, 5, 7 +ginaturalminor ftgen 0, 0, 8, -2, 0, 2, 3, 5, 7, 8, 10, 12 +gineapolitanmajor ftgen 0, 0, 8, -2, 0, 1, 3, 5, 7, 9, 11, 12 +gineapolitanminor ftgen 0, 0, 8, -2, 0, 1, 3, 5, 7, 8, 11, 12 +giocta_1_2 ftgen 0, 0, 8, -2, 0, 1, 3, 4, 6, 7, 9, 10 +giocta_2_1 ftgen 0, 0, 8, -2, 0, 2, 3, 5, 6, 8, 9, 11 +gioriental ftgen 0, 0, 8, -2, 0, 1, 4, 5, 6, 9, 10, 12 +giovertone ftgen 0, 0, 8, -2, 0, 2, 4, 6, 7, 9, 10, 12 +gipa ftgen 0, 0, 6, -2, 0, 2, 3, 7, 8, 12 +gipb ftgen 0, 0, 6, -2, 0, 1, 3, 6, 8, 12 +gipd ftgen 0, 0, 6, -2, 0, 2, 3, 7, 9, 12 +gipe ftgen 0, 0, 6, -2, 0, 1, 3, 7, 8, 12 +gipelog ftgen 0, 0, 6, -2, 0, 1, 3, 7, 10, 12 +gipentamaj ftgen 0, 0, 5, -2, 0, 2, 4, 7, 9 +gipentamin ftgen 0, 0, 5, -2, 0, 3, 5, 7, 10 +gipentatonicmajor ftgen 0, 0, 6, -2, 0, 2, 4, 7, 9, 12 +gipentatonicminor ftgen 0, 0, 6, -2, 0, 3, 5, 7, 10, 12 +gipersian ftgen 0, 0, 8, -2, 0, 1, 4, 5, 6, 8, 11, 12 +gipfcg ftgen 0, 0, 6, -2, 0, 2, 4, 7, 9, 12 +giphrygian ftgen 0, 0, 7, -2, 0, 1, 3, 5, 7, 8, 10 +giphrygian ftgen 0, 0, 8, -2, 0, 1, 3, 5, 7, 8, 10, 12 +giphrygianmajor ftgen 0, 0, 8, -2, 0, 1, 4, 5, 7, 8, 10, 12 +giquarter ftgen 0, 0, 22, -2, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7, 7.5, 8, 8.5, 9, 9.5, 10, 10.5, 11, 11.5 +giromanian ftgen 0, 0, 8, -2, 0, 2, 3, 6, 7, 9, 10, 12 +gispanish ftgen 0, 0, 8, -2, 0, 1, 4, 5, 7, 8, 10, 12 +gispanish8tone ftgen 0, 0, 9, -2, 0, 1, 3, 4, 5, 6, 8, 10, 12 +gisymmetrical ftgen 0, 0, 9, -2, 0, 1, 3, 4, 6, 7, 9, 10, 12 +gitodi ftgen 0, 0, 8, -2, 0, 1, 3, 6, 7, 8, 11, 12 +giwhole ftgen 0, 0, 6, -2, 0, 2, 4, 6, 8, 10 +giwhole ftgen 0, 0, 7, -2, 0, 2, 4, 6, 8, 10, 12 +gibp ftgen 0, 0, 12, -2, 0, 1.3324, 3.0185, 4.3508, 5.8251, 7.3693, 8.8436, 10.1760, 11.6502, 13.1944, 14.6687, 16.0011, 17.6872, 19.0196 + +#define chinese #gichinese# +#define persian #gipersian# +#define indian #giindian# +#define arabian #giarabian# +#define minor #giminor# +#define major #gimajor# + +opcode scale_up, k, ikkk + iscale, kmin, kmax, kspeed xin + ktrig = metro:k(kspeed) + idiff = abs( i(kmax) - i(kmin) ) + ibase = i(kmin) + kpick init 0 + knote init ibase + kitv init 0 + ilen = ftlen(iscale) + if(ktrig == 1) then + kpick = (kpick + 1) ;% ilen + if(kpick == ilen) then + kpick = 0 + kitv = 0 + endif + kitv = table(kpick, iscale) - kitv + printk2 kitv + knote = knote + kitv + if(knote > kmax) then + knote = kmin + (knote - kmax) + endif + endif + xout knote +endop + +/* +opcode scale_up, k, ikkk + iscale, kmin, kmax, kspeed xin + ktrig = metro:k(kspeed) + idiff = abs( i(kmax) - i(kmin) ) + ibase = i(kmin) + (idiff/2) + kpick init 0 + knote init ibase + ilen = ftlen(iscale) + if(ktrig == 1) then + kpick = (kpick + 1) % ilen + kitv = table(kpick, iscale) + knote = knote + kitv + if(knote > kmax) then + knote = kmin + (knote - kmax) + endif + endif + xout knote +endop + +*/ + +opcode scale_down, k, ikkk + iscale, kmin, kmax, kspeed xin + ktrig = metro:k(kspeed) + + idiff = abs( i(kmax) - i(kmin) ) + ibase = i(kmax) + kpick init 0 + knote init ibase + ilen = ftlen(iscale) + if(ktrig == 1) then + kpick = (kpick + 1) % ilen + kitv = table(kpick, iscale) + knote = knote - kitv + if(knote < kmin) then + knote = kmax ;- (kmin - knote) + endif + endif + xout knote +endop + +opcode scale_random, k, ikkk + iscale, kmin, kmax, kspeed xin + ktrig = metro:k(kspeed) + idiff = abs( i(kmax) - i(kmin) ) + ibase = i(kmin) + (idiff/2) + kpick init 0 + knote init ibase + ilen = ftlen(iscale) + if(ktrig == 1) then + ksign = (rint:k(0, 1) == 0) ? -1 : 1 + kpick = (kpick + 1) % ilen + kitv = table(kpick, iscale) + knote = knote + (kitv * ksign) + if(knote < kmin) then + knote = kmax ;- (kmin - knote) + endif + if(knote > kmax) then + knote = kmin ;+ (knote - kmax) + endif + endif + xout knote +endop diff --git a/public/system-files/synth.orc b/public/system-files/synth.orc new file mode 100644 index 0000000..5c58d84 --- /dev/null +++ b/public/system-files/synth.orc @@ -0,0 +1,1039 @@ +instr basic_sine + inote init p4 + ifq = mtof:i(inote) + iamp = p5 + kenv = expsegr(1.01, p3, 0.01) - 0.01 + ao = oscili(iamp, ifq) * kenv * 0.4 + chnmix(ao, "out1") + chnmix(ao, "out2") +endin + +opcode basic_sine_gen, 0, kk + ktrig, kamp xin + kamp = limit:k(kamp, 0, 1) + ktrig = limit:k(ktrig, 0, 1) + if(check_trig(ktrig) == 1) then + schedulek("basic_sine", 0, random:k(0.03, 0.1), random:k(50, 80), kamp * ktrig) + endif +endop + +instr pop_impl + inote init p4 + ifq = mtof:i(inote) + iamp = p5 + kenv = expsegr(1.01, p3, 0.01) - 0.01 + kmod = oscili:k(ifq / 100, 40) + ao = vco2(iamp, ifq + kmod) * kenv + af = K35_lpf(ao, ifq*4, 8) * 0.3 + chnmix(af, "out1") + chnmix(af, "out2") +endin + +opcode pop, 0, kkkk + ktrig, kamp, knote, kdur xin + kamp = limit:k(kamp, 0, 1) + knote = limit:k(knote, 0, 100) + 27 + kdur = to_tempo_dur(limit:k(kdur, 0.01, 1)) + ktrig = limit:k(ktrig, 0, 1) + if(check_trig(ktrig) == 1) then + schedulek("pop_impl", 0, kdur, knote, kamp * ktrig) + endif +endop + + +instr basic_kick + iamp init p4 + $FQPERC + + ihi = random:i(100, 600) ; 600 + ilo = random:i(100, 800) ; 20 + kfq = expseg(ihi, p3, ilo) + ao = vco2(1, kfq) + af = K35_lpf(ao, 100, 3) + amod = oscili(100, 2) + acar = oscili(1, abs(amod) + 250) + abnd = butterhp(acar, 400) + klofq = expseg(2000, p3/4, 20) + alo = K35_lpf(abnd, klofq, 5) * 0.5 + aenv = expseg:a(1.01, p3/4*3, 1.01, p3/4, 0.01) - 0.01 + akick = (alo + af) * aenv * iamp + + adist = distort1(akick, ksavage*3, 1, 1 / (abs(gklorenz_x*3) + 1), 1 - abs(gklorenz_z)) + kdist_mult = isavage_mode > 0 ? 1 : 0 + + adelayed init 0 + kfb = mirror:k(abs(gklorenz_y) + 0.5, 0, 0.9) + adel = interp( ( mirror:k( lineto(abs(ksavage), 0.1) , 0.2, 0.999 ) * 1000)) + adelayed = vdelay(akick + (adelayed * kfb), adel, 1500) + krel = linsegr:k(1, 1, 1, 1, 0) + + akick += (adelayed * kdist_mult) * krel + chnmix(akick, "out1") + chnmix(akick, "out2") +endin + +opcode kick, 0, kkO + ktrig, kamp, ksav xin + kamp = limit:k(kamp, 0, 1) + ktrig = limit:k(ktrig, 0, 1) + + ksav = rint:i(1, 5) + if(check_trig(ktrig) == 1) then + schedulek("basic_kick", 0, 0.1, kamp * ktrig, 0) + endif +endop + +instr noisy_hat + asig = pinker() + aenv = expseg(1.01, p3, 0.01) - a(0.01) + ifq init p4 + iamp init p5 + isavage_mode init p6 + $SAVAGE(isavage_mode') + kfq = ifq + (ifq * ksavage) + af = K35_hpf(asig, kfq, 9) * aenv * iamp + + adelayed init 0 + kfb = mirror:k(abs(gklorenz_y) + 0.5, 0, 0.9) + adel = interp( ( mirror:k( lineto(abs(ksavage), 0.1) , 0.2, 0.999 ) * 1000)) + adelayed = vdelay3(af + (adelayed * kfb), adel, 1000) + krel = linsegr:k(1, 1, 1, 1, 0) + + ksav_mult = (isavage_mode > 0) ? 1 : 0 + amix = af + (adelayed * ksav_mult) + + chnmix(amix, "out1") + chnmix(amix, "out2") +endin + +opcode hihat, 0, kkkkO + ktrig, kamp, knote, kdur, ksav xin + kamp = limit:k(kamp, 0, 1) + kdur = to_tempo_dur(limit:k(kdur, 0.001, 100)) + kfq = mtof(limit:k(knote, 0, 100) + 27) + ktrig = limit:k(ktrig, 0, 1) + if(check_trig(ktrig) == 1) then + schedulek("noisy_hat", 0, kdur, kfq, kamp * ktrig, ksav) + endif +endop + +instr bass_impl + iamp init p4 + ifq init p5 + imodspeed init p6 + ao = vco2(0.8, ifq) + aenv = expsegr:a(0.01, 0.05, 1.01, p3 - 0.05, 1.01, p3/16, 0.01) - a(0.01) + + imult = rint:i(1, 8) + if(imodspeed >= 0) then + imult = imodspeed + endif + kmodfq = gktempo * imult + kcut = poscil(0.5, kmodfq) + 0.5 + afilt = K35_lpf(ao, limit:k(kcut * (ifq * 20) + 20, 0, 12000), 6) + asynt = afilt * aenv * iamp * 0.2 + + ipan = random:i(0.25, 0.75) + a1, a2 pan2 asynt, ipan + chnmix(a1, "out1") + chnmix(a2, "out2") +endin + +opcode wah, 0, kkkkJ + ktrigger, kamp, knote, kdur, kmodspeed xin + kamp = limit:k(kamp, 0, 1) + kdur = to_tempo_dur(limit:k(kdur, 0.001, 1000)) + kfq = mtof(limit:k(knote, 0, 100) + 20) + ktrigger = limit:k(ktrigger, 0, 1) + kmodspeed = limit:k(kmodspeed, -1, 100) + if(check_trig(ktrigger) == 1) then + schedulek("bass_impl", 0, kdur, kamp * ktrigger, kfq, kmodspeed) + endif +endop + +instr glide_impl + iamp init p4 + ifqcenter init p5 + isavage_mode init p6 + $SAVAGE(isavage_mode') + ifqstart = random:i(ifqcenter - (ifqcenter/2), ifqcenter + (ifqcenter / 2)) + ifqend = random:i(ifqcenter - (ifqcenter/2), ifqcenter + (ifqcenter / 2)) + kfq = expseg(ifqstart, p3, ifqend) + kfq = kfq + (ksavage * kfq) + ;aenv = expsegr:a(0.01, p3/8, 1.01, p3/8*7, 1.01, p3/4, 0.01) - a(0.01) + aenv = expsegr:a(0.01, p3, 1.01, p3/4, 0.01) - a(0.01) + ao = vco2(1, kfq) + afilt = K35_lpf(ao*aenv, kfq * 2, 7) * iamp * 0.3 + chnmix(afilt, "out1") + chnmix(afilt, "out2") +endin +maxalloc("glide", 12) + +opcode glide, 0, kkkkO + ktrig, kamp, knote, kdur, ksav xin + kdensity = 4 + kamp = limit:k(kamp, 0, 1) + kfq = mtof(limit:k(knote, 0, 100) + 20) + ktrig = limit:k(ktrig, 0, 1) + if(check_trig(ktrig) == 1) then + kcnt = 0 + while kcnt < kdensity do + schedulek("glide_impl", 0, to_tempo_dur(kdur), kamp * ktrig, kfq, ksav) + kcnt += 1 + od + endif +endop + +instr trumpet_impl + aenv = expseg:a(1.01, p3, 0.01) - a(0.01) + ;kff init p4 + kff = p4 + isavage_mode init p8 + $SAVAGE(isavage_mode') + kff = kff + (ksavage * kff) + ao = vco2(p5, kff + oscili:k(3, 0.1) ) * aenv + ;ao2 = vco2(p5 * 0.7, kff + oscili:k(20, 0.15) ) * aenv + ;ao3 = vco2(p5, kff * 1.9 + oscili:k(20, 0.15) ) * aenv + anul = a(0) + irnd = random:i(0.01, 0.2) + kml = (p6 == 1) ? 1 : 0 + kmb = (p6 == 2) ? 1 : 0 + kmh = (p6 == 3) ? 1 : 0 + af = spf(ao * kml, ao * kmb, ao * kmh, aenv * 500 + 400 + abs(oscili:k(1000, irnd)), p7) + af *= 0.2 + kpan = rspline(0, 1, 0.1, 2) + a1, a2 pan2 af, kpan + chnmix(a1, "out1") + chnmix(a2, "out2") +endin + +opcode ping, 0, kkkkO + ktrig, kamp, knote, kdur, ksav xin + kamp = limit:k(kamp, 0, 1) + kfq = mtof(limit:k(knote, 0, 100) + 20) + kdur = limit:k(kdur, 0.001, 1000) + if(check_trig(ktrig) == 1) then + schedulek("trumpet_impl", 0, to_tempo_dur(kdur), kfq, kamp, rint:k(1, 3), random:k(1, 4), ksav) + endif +endop + +instr wave_impl + ; pluck env + ;ares repluck iplk, kamp, icps, kpick, krefl, axcite + axcite init 1 + axcite = linseg:a(1, 0.01, 0) + krefl = 0.5 + kpick = 0.1 + icps = 400 + kamp = 1 + iplk = 0.8 + apluck = follow2(repluck(iplk, kamp, icps, kpick, krefl, axcite), 0.01, 0.05) + ; ares follow2 asig, katt, krel + isavage_mode init p6 + $SAVAGE(isavage_mode') + + ifq init p5 + iamp init p4 + + kfq1 = ifq + oscili:k(ifq / 100, rspline:k(0.01, 5, 1, 5)) + kfq1 = kfq1 + (kfq1 * ksavage) + kfq2 = (ifq * 0.99) + oscili:k(ifq / 100, rspline:k(0.01, 5, 1, 5)) + kfq2 = kfq2 + (kfq2 * ksavage) + ao = vco2(1/2, kfq1) + ao += vco2(1/2, kfq2, 12) + aenv = expsegr:a(0.01, p3/2, 1.01, p3/2, 1.01, p3, 0.01) - a(0.01) + kfb = 0.5 + adel = a(0.02) + ;adel = rspline:a(0.01, 0.2, 0.01, 0.2) + af = flanger(ao * aenv, adel, kfb) * iamp * 0.2 + kpan = rspline:k(0, 1, 0.1, 3) + a1, a2 pan2 af, kpan + chnmix(a1, "out1") + chnmix(a2, "out2") +endin + +opcode wave, 0, kkkkO + ktrig, kamp, knote, kdur, ksav xin + kfq = mtof(limit:k(knote, 0, 100) + 20) + kamp = limit:k(kamp, 0, 1) + ktrig = limit:k(ktrig, 0, 1) + kdur = to_tempo_dur(limit:k(kdur, 0.001, 1000)) + if(check_trig(ktrig) == 1) then + schedulek("wave_impl", 0, kdur, kamp, kfq, ksav) + endif +endop + +instr pluck_impl + ; pluck env + ;ares repluck iplk, kamp, icps, kpick, krefl, axcite + aseg = linsegr:a(0, 0.01, 0.1, p3 - 0.01, 0.3, p3/4, 1) + aenv = tablei:a(aseg, 1, 1) + imult init p6 + iamp init p4 + inote init p5 + if(imult <= 0) then + imult = rint:i(1, 10) + endif + + isavage_mode init p7 + $SAVAGE(isavage_mode') + + ifq1 = mtof:i(inote + (imult * 0)); / ((1) * 2) + ifq2 = mtof:i(inote + (imult * 1)); / ((2) * 2) + ifq3 = mtof:i(inote + (imult * 2)); / ((3) * 2) + ifq4 = mtof:i(inote + (imult * 3)); / ((4) * 2) + + kfq1 = ifq1 + (ksavage * ifq1) + kfq2 = ifq2 + (ksavage * ifq2) + kfq3 = ifq3 + (ksavage * ifq3) + kfq4 = ifq4 + (ksavage * ifq4) + + + imult = 1 - (inote / 100) + imult *= (imult * imult) + imult = (imult * 15) + 5 + + aosc = vco2(1/4, kfq1) + vco2(1/4, kfq2) + vco2(1/2, kfq3) + vco2(1/4, kfq4) + aosc *= aenv + af = K35_lpf(aosc, limit:a(aenv * ifq1 * imult, 0, 10000), 7) * iamp * 0.5 + chnmix(af, "out1") + chnmix(af, "out2") +endin + + ; faire une table karplustrong + ; trouver une solution pour le "mult" (random ? init ? harmonie ? ) +opcode chord, 0, kkkkJO + ktrig, kamp, knote, kdur, kmult, ksav xin + ktrig = limit:k(ktrig, 0, 1) + kamp = limit:k(kamp ,0, 1) + knote = limit:k(knote, 0, 100) + 20 + kdur = to_tempo_dur(limit:k(kdur, 0.001, 1000)) + kmult = limit:k(kmult, -1, 10) + if(check_trig(ktrig) == 1) then + schedulek("pluck_impl", 0, kdur, kamp, knote, kmult, ksav) + endif +endop + + +opcode funkymod, k, kkk + kamp, kfq, kduty xin + + ; 1 sine + ; 2 tri + ; 3 square + ; 4 saw up + ; 5 saw down + ; 6 pwm + iwave = rint:i(1, 6) + if(iwave == 3) then + iwave = 4 + elseif(iwave == 6) then + iwave = 5 + endif + + klf_amp = (iwave < 6) ? 1 : 0 + kpwm_amp = 1 - klf_amp + + klfo = lfo:k(kamp, kfq) + if(iwave < 3) then + klfo += kamp + klfo /= 2 + endif + + klfo *= klf_amp + + kpwm = vco2(kamp*0.5, kfq, 2, kduty) + (kamp * 0.5) + kpwm *= kpwm_amp + xout klfo + kpwm +endop + + +instr buzzy_impl + iwavet[] fillarray 0, 2, 4, 12 + iwavep = rint:i(0, lenarray(iwavet)-1) + iwave = iwavet[iwavep] + kpw = rspline(0, 1, 0.25, 4) + + ifq init p4 + kfq = ifq + rspline:k(-(ifq/1000), ifq/1000, 0.01, 0.3) + + isavage_mode init p6 + $SAVAGE(isavage_mode') + kfq = kfq + (kfq * ksavage) + + ao = vco2(0.3, kfq, iwave, kpw ) * (expseg(1.01, p3, 0.01) - 0.01) + + ahp,alp,abp,abr svn ao, kfq, 10, 0.5 + iamp init p5 + + ipick = rint:i(1, 4) + ihp = (ipick == 1) ? 1 : 0 + ilp = (ipick == 2) ? 1 : 0 + ibp = (ipick == 3) ? 1 : 0 + ibr = (ipick == 4) ? 1 : 0 + ares = (ahp * ihp) + (alp * ilp) + (abp * ibp) + (abr * ibr) + + imodfq = random:i(0.5, 4) + imodamp = random:i(0.2, 1) + kduty = rspline:k(0, 1, 0.2, 4) + kmod = funkymod(imodamp, imodfq, kduty) + ares = (ares * kmod) + (ares * (1 - imodamp)) + ares *= iamp + ipan = random:i(0, 1) + al, ar pan2 ares, ipan + outs al, ar +endin + + +opcode buzzy, 0, kkkkO + ktrig, kamp, knote, kdur, ksav xin + ktrig = limit:k(ktrig, 0, 1) + kamp = limit:k(kamp ,0, 1) + knote = limit:k(knote, 0, 100) + 20 + kdur = to_tempo_dur(limit:k(kdur, 0.001, 1000)) + if(check_trig(ktrig) == 1) then + schedulek("buzzy_impl", 0, kdur, mtof:k(knote), kamp, ksav) + endif +endop + + +instr fnoise_impl + iamp init p5 + ifqmult init 4 + ifqoffset init p4 + + kcrv = expsegr:k(0.01, p3/4, 1.01, p3, 0.01) - 0.01 + + kres = kcrv * 0.7 + kcf = kcrv * ifqmult + ifqoffset + ao = noise(0.5, 0.5) * kcrv + ao *= kcrv + afilt = vclpf(ao, kcf, kres) + asig = afilt + + kpan = rspline:k(0, 1, 0.01, 0.4) + a1, a2 pan2 asig, kpan + outs a1, a2 +endin + +opcode noisy, 0, kkkk + ktrig, kamp, knote, kdur xin + ktrig = limit:k(ktrig, 0, 1) + kamp = limit:k(kamp ,0, 1) + knote = limit:k(knote, 0, 100) + 20 + kdur = to_tempo_dur(limit:k(kdur, 0.001, 1000)) + if(check_trig(ktrig) == 1) then + schedulek("fnoise_impl", 0, kdur, mtof:k(knote), kamp) + endif +endop + +#define PI #3.141592653589793# + +opcode audio_ltone, a, a + ain xin + aout init 0 + kcnt = 0 + while kcnt < ksmps do + aout[kcnt] = (ain[kcnt] < 1) ? 1 : 0 + kcnt += 1 + od + xout aout +endop + +gipulsar_env = ftgen(0, 0, 16384, -16, 0, 682, -3.606, 0.99403, 4588, 3.8776, 0.47761, 11112, -6.1075, 0) + +instr pulsar_impl + ifq init p5 + iform_fq init p6 + isine_cycles init p7 + iamp init p4 + + aenv = tablei:a(linseg:a(0, p3, 1), gipulsar_env, 1) + + kform_fq = (iform_fq) + randomi:k(0, iform_fq / 10, 0.2) + + apulsaret_phase = phasor:a(ifq) * kform_fq / ifq + agate = audio_ltone(apulsaret_phase) ;(apulsaret_phase < 1) ? 1 : 0 + awin = pow(apulsaret_phase, 4) + asine = sin:a(apulsaret_phase * 2 * $PI * isine_cycles) * awin * agate + asine *= iamp * 0.7 * aenv + ipan = random:i(0.25, 0.75) + al, ar pan2 asine, ipan + outs al, ar +endin + +opcode pulsar, 0, kkkkkk + ktrig, kamp, knote, kdur, kformant_fq, ksine_cycles xin + ktrig = limit:k(ktrig, 0, 1) + kamp = limit:k(kamp ,0, 1) + knote = limit:k(knote, 0, 100) + 20 + kdur = to_tempo_dur(limit:k(kdur, 0.001, 1000)) + + kformant_fq = limit:k(kformant_fq, 0, 10) * 150 + 300 + ksine_cycles = int(limit:k(ksine_cycles, 1, 10)) + if(check_trig(ktrig) == 1) then + schedulek("pulsar_impl", 0, kdur, kamp, mtof:k(knote), kformant_fq, ksine_cycles) + endif +endop + +gigraou_env = ftgen(0, 0, 16384, -16, 0, 185, -3.606, 1, 3125, 4.7463, 0.40746, 13072, -8.0925, 0) + +gaGraou[] init 2 +instr graou_out + afbl init 0 + afbr init 0 + al = gaGraou[0] + ar = gaGraou[1] + + imaxdel init 2000 + adL = randomi:a(100, 500, 0.1) + adR = randomi:a(125, 525, 0.1) + adelL = vdelay(al+afbl, adL, imaxdel) + adelR = vdelay(ar+afbr, adR, imaxdel) + ifb init 0.6 + afbl = adelL * ifb + afbr = adelR * ifb + gaGraou[0] = 0 + gaGraou[1] = 0 + outs al+adelL, ar+adelR +endin +schedule("graou_out", 0, -1) + +instr graou_impl + iamp init p4 + ifq init p5 + iQ init p6 + aosc = vco2:a(1, ifq) + kfilt_env = expseg:k(5000, p3, 50) + afilt = K35_lpf(aosc, kfilt_env, iQ) + aenv = tablei:a(linseg:a(0, p3, 1), gigraou_env, 1) + afilt *= aenv * iamp * 0.4 + + ipan = random:i(0.25, 0.75) + al, ar pan2 afilt, ipan + gaGraou[0] = gaGraou[0] + al + gaGraou[1] = gaGraou[1] + ar +endin + + +opcode piou, 0, kkkkk + ktrig, kamp, knote, kdur, kQ xin + ktrig = limit:k(ktrig, 0, 1) + kamp = limit:k(kamp ,0, 1) + knote = limit:k(knote, 0, 100) + 20 + kdur = to_tempo_dur(limit:k(kdur, 0.001, 1000)) + kQ = limit:k(kQ, 1, 10) + + if(check_trig(ktrig) == 1) then + schedulek("graou_impl", 0, kdur, kamp, mtof:k(knote), kQ) + endif +endop + +instr smooth_wave_instr + inote init p4 + iamp init p5 + $FQ + acrv = transeg:a(0, p3/3, 3, 1, p3/3*2, -3, 0) + + ;acrv = tablei:a(linseg:a(0, p3, 1), icrv, 1) * linseg:a(0, p3/100, 1, p3/100*98, 1, p3/100, 0) + kvib = poscil:k( expseg:k(random:i(0, ifq/30), p3, random:i(0, ifq/30)), expseg:k(random:i(0.01, 10), p3, random:i(0.01, 10))) + asig = vco2(0.8, kfq + kvib, 10) + + abnd = bqrez:a(asig, kfq, 50, 3) + aout = balance(abnd, asig) * (acrv * iamp) + + ipan = random:i(0, 1) + aL, aR pan2 aout * gkgain, ipan + chnmix(aL, "out1") + chnmix(aR, "out2") +endin + +opcode buzzwave, 0, kkkkO + ktrig, kamp, knote, kdur, ksav xin + ktrig = limit:k(ktrig, 0, 1) + kamp = limit:k(kamp ,0, 1) + knote = limit:k(knote, 0, 100) + 20 + kdur = to_tempo_dur(limit:k(kdur, 0.001, 1000)) + if(trigger:k(ktrig, 0.5, 0) == 1) then + schedulek("smooth_wave_instr", 0, kdur, knote, kamp, ksav) + endif +endop + +instr smooth_noise_instr + inote init p4 + iamp init p5 + + $FQ + + afb init 0 + + acrv = transeg:a(0, p3/3, 3, 1, p3/3*2, -3, 0) + kvib = oscili:k( expseg:k(random:i(0, ifq/30), p3, random:i(0, ifq/30)), expseg:k(random:i(0.1, 15), p3, random:i(0.1, 15))) + asig = pinker() + + kfreq = kfq + kvib + + abnd = bqrez:a( bqrez:a(asig, kfreq, 70, 2), kfreq * 2, 70, 2) + adist = distort1(abnd, 2, 1, 0.15, 0.15) + aout = balance(adist, asig) * (acrv * iamp) + + ;adel = abs(oscili:a(200, expseg:a(random:i(0.1, 15), p3, random:i(0.1, 15)))) + adelayed = vdelay3(aout + afb, a(100) , 1000) + afb = adelayed * 0.2 + + ares = (adelayed + aout) + ipan = random:i(0, 1) + aL, aR pan2 ares * 5 * gkgain, ipan + chnmix(aL, "out1") + chnmix(aR, "out2") +endin + +opcode noisywave, 0, kkkkO + ktrig, kamp, knote, kdur, ksav xin + ktrig = limit:k(ktrig, 0, 1) + kamp = limit:k(kamp ,0, 1) + knote = limit:k(knote, 0, 100) + 20 + kdur = to_tempo_dur(limit:k(kdur, 0.001, 1000)) + if(trigger:k(ktrig, 0.5, 0) == 1) then + schedulek("smooth_noise_instr", 0, kdur, knote, kamp, ksav) + endif +endop + +; very chaotic nonlinear moving waveform & timber & modulation +instr crunchy_instr + inote init p4 + iamp init p5 + $FQ + + ao = vco2:a(iamp, kfq) + oscili:a(iamp * 0.5, kfq * 0.5) + afilt = K35_lpf(ao, abs(galorenz_x)*2000+100, abs(galorenz_z) * 5 + 1.05 ) + + kshape1 = 0.1;lag(abs( gklorenz_y) * 0.1, 1) + kshape2 = 0.1;lag(abs(gklorenz_z) * 0.1, 1) + kpregain = mirror:k(gklorenz_x * gklorenz_y, 0.03, 1) * 500 + kpostgain = 1 / limit:k(kpregain, .2, 25);0.1 + adist = lag(distort1(afilt, kpregain, kpostgain, kshape1, kshape2) , 0.003) + + aenv = (expseg:a(1.01, p3, 0.01) - a(0.01)) + adist *= aenv * iamp * 0.8 + kpan = gkmirlorenz_y + aL, aR pan2 adist * gkgain, kpan + chnmix(aL, "out1") + chnmix(aR, "out2") +endin + +opcode crunchy, 0, kkkkO + ktrig, kamp, knote, kdur, ksav xin + ktrig = limit:k(ktrig, 0, 1) + kamp = limit:k(kamp ,0, 1) + knote = limit:k(knote, 0, 100) + 20 + kdur = to_tempo_dur(limit:k(kdur, 0.001, 1000)) + if(check_trig(ktrig) == 1) then + schedulek("crunchy_instr", 0, kdur, knote, kamp, ksav) + endif +endop + +instr crunchyslow_instr + inote init p4 + iamp init p5 + $FQ + ao = vco2:a(iamp, kfq) + oscili:a(iamp * 0.5, kfq * 0.5) + vco2(iamp*0.25, kfq * 3) + afilt = K35_lpf(ao, mirror:a(galorenz_x, 0, 1)*5000+100, abs(galorenz_z) * 5 + 1.05 ) + + kshape1 = 0.1;lag(abs( gklorenz_y) * 0.1, 1) + kshape2 = 0.1;lag(abs(gklorenz_z) * 0.1, 1) + kpregain = mirror:k(gklorenz_x * gklorenz_y, 0.03, 1) * 350 + ;kpostgain = 0.1 + kpostgain = 1 / limit:k(kpregain, .2, 25);0.1 + + adist = lag(distort1(afilt, kpregain, kpostgain, kshape1, kshape2) , 0.003) + + aenv = (expseg:a(0.01, p3/2, 1.01, p3/2, 0.01) - a(0.01)) + adist *= aenv * iamp + ipan_base = rint:i(0, 1) + kpan = gkmirlorenz_y + if(ipan_base > 0) then + kpan = 1 - kpan + endif + aL, aR pan2 adist * gkgain, kpan + chnmix(aL, "out1") + chnmix(aR, "out2") +endin + +opcode crunchy_wav, 0, kkkkO + ktrig, kamp, knote, kdur, ksav xin + ktrig = limit:k(ktrig, 0, 1) + kamp = limit:k(kamp ,0, 1) + knote = limit:k(knote, 0, 100) + 20 + kdur = to_tempo_dur(limit:k(kdur, 0.001, 1000)) + if(check_trig(ktrig) == 1) then + schedulek("crunchyslow_instr", 0, kdur, knote, kamp, ksav) + endif +endop + +opcode darkwave, 0, kkkkO + ktrig, kamp, knote, kdur, ksav xin + crunchy_wav(ktrig, kamp, knote, kdur, ksav) +endop + +instr shinywave_instr + inote init p4 + iamp init p5 + $FQ + ao = vco2:a(iamp, kfq) + oscili:a(iamp * 0.5, kfq * 0.5) + vco2(iamp*0.45, kfq * 2) + vco2(iamp*0.3, kfq*3) + ;afilt = K35_lpf(ao, mirror:a(galorenz_x, 0, 1)*5000+100, abs(galorenz_z) * 5 + 1.05 ) + + kshape1 = 0;lag(abs( gklorenz_y) * 0.1, 1) + kshape2 = 0; lag(abs(gklorenz_z) * 0.1, 1) + kpregain = abs( lag( mirror:k(gklorenz_x * gklorenz_y, 0.03, 1) , 0.5) ) * 500 + kpostgain = 0.1 + adist = lag(distort1(ao, kpregain, kpostgain, kshape1, kshape2) , 0.003) + + aenv = (expsegr:a(0.01, p3, 1.01, 0.05, 0.01) - a(0.01)) + adist *= aenv * iamp * 0.7 + ipan_base = rint:i(0, 1) + kpan = gkmirlorenz_y + if(ipan_base > 0) then + kpan = 1 - kpan + endif + aL, aR pan2 adist * gkgain, kpan + chnmix(aL, "out1") + chnmix(aR, "out2") +endin + +opcode brightwave, 0, kkkkO + ktrig, kamp, knote, kdur, ksav xin + ktrig = limit:k(ktrig, 0, 1) + kamp = limit:k(kamp ,0, 1) + knote = limit:k(knote, 0, 100) + 20 + kdur = to_tempo_dur(limit:k(kdur, 0.001, 1000)) + if(check_trig(ktrig) == 1) then + schedulek("shinywave_instr", 0, kdur, knote, kamp, ksav) + endif +endop + +; very chaotic nonlinear moving waveform & timber & modulation +instr glide_instr + inote init p4 + iamp init p5 + $FQ + + ibase = random:i(0.01, 0.4) * ifq + kglide = transeg:k(ibase, p3/4, -6, ifq) + (ksavage * ifq) + + ao = vco2:a(iamp, kglide) + oscili:a(iamp * 0.5, kglide * 0.5) + vco2(iamp * 0.3, kglide * 2, 10) + ;afilt = K35_lpf(ao, abs(galorenz_x)* 10000+500, abs(galorenz_z) * 3 + 1.05 ) + + kshape1 = lag(abs( gklorenz_y) * 0.1, 1) + kshape2 = lag(abs(gklorenz_z) * 0.1, 1) + kpregain = abs( lag( mirror:k(gklorenz_y * gklorenz_z, 0.03, 1) , 1) ) * 500 + kpostgain = 0.1 + adist = lag(distort1(ao, kpregain, kpostgain, kshape1, kshape2) , 0.003) + + aenv = (expseg:a(0.01, 0.02, 1.01, p3 - 0.02, 0.01) - a(0.01)) + adist *= aenv * iamp * 0.3 + kpan = gkmirlorenz_z ;mirror:a(abs(galorenz_y), 0, 1) + aL, aR pan2 adist * gkgain, kpan + chnmix(aL, "out1") + chnmix(aR, "out2") +endin + +opcode glidy, 0, kkkkO + ktrig, kamp, knote, kdur, ksav xin + ktrig = limit:k(ktrig, 0, 1) + kamp = limit:k(kamp ,0, 1) + knote = limit:k(knote, 0, 100) + 20 + kdur = to_tempo_dur(limit:k(kdur, 0.001, 1000)) + if(check_trig(ktrig) == 1) then + schedulek("glide_instr", 0, kdur, knote, kamp, ksav) + endif +endop + +instr snare_impl + + idur init p3 ; Duration + idyn init p4 ; Amplitude + ifqc init p5 ; Pitch to frequency + irez init p6 ; Tone + ispdec init p7 ; Spring decay + ispton init p8 ; Spring tone + ispmix init p9 ; Spring mix + ispq init p10 ; Spring Q + ipbnd init p11 ; Pitch bend + ipbtm init p12 ; Pitch bend time + + + kdclk linseg 1, idur-.002, 1, .002, 0 ; Declick envelope + adyn linseg 1, .2/ifqc, 1, .2/ifqc, 0, idur-.002, 0 ; An amplitude pulse + kptch linseg 1, ipbtm, ipbnd, ipbtm, 1, .1, 1 + + aosc1 vco 1, ifqc, 2, 1, 1, 1 ; Use a pulse of the vco to stimulate the filters + aosc = -aosc1*adyn ; Multiply by the envelope pulse + aosc2 butterlp aosc, 12000 ; Lowpass at 12K to take the edge off + + asig1 moogvcf aosc, ifqc*kptch, .9*irez ; Moof filter with high resonance for basic drum tone + asig2 moogvcf aosc*.5, ifqc*2.1*kptch, .75*irez ; Sweeten with an overtone + + adynr expseg .1, .002, 1, .2, .005 + + apink1 pinkish 2, 0, 20;, iseed ; Use multi-rate pink noise + apink2 pinkish 2, 0, 20;, iseed ; Use multi-rate pink noise + + apink1 = apink1*2*asig1 + arndr1 delay apink1-apink2*.6, .01 + + ahp1l rezzy apink1+arndr1, 2700*ispton*kptch, 5*ispq, 1 ; High pass rezzy based at 2700 + ahp2l butterbp apink1, 2000*ispton*kptch, 500/ispq ; Generate an undertone + ahp3l butterbp apink1, 5400*ispton*kptch, 500/ispq ; Generate an overtone + ahpl pareq ahp1l+ahp2l*.7+ahp3l*.3, 15000, .1, .707, 2 ; Attenuate the highs a bit + + ; Mix drum tones, pulse and noise signal & declick + aoutl = (asig1+asig2+aosc2*.1+ahpl*ispmix*4)*idyn*kdclk + + isavage_mode init p13 + $SAVAGE(isavage_mode') + adelayed init 0 + kfb = mirror:k(abs(gklorenz_y) + 0.5, 0, 0.9) + adel = limit:a( interp( ( mirror:k( lineto(abs(ksavage), 0.1) , 0.2, 0.999 ) * 1000)), 0.01, 1000) + + adelayed = vdelay(aoutl + (adelayed * kfb), adel, 1500) + krel = linsegr:k(1, 1, 1, 1, 0) + + ksav_mult = (isavage_mode > 0) ? 1 : 0 + amix = (aoutl + (adelayed * ksav_mult)) * krel + + ;outall aoutl + chnmix(amix, "out1") + chnmix(amix, "out2") +endin + +opcode snare, 0, kkO + ktrig, kamp, ksav xin + ktrig = limit:k(ktrig, 0, 1) + kamp = limit:k(kamp ,0, 1) + if(check_trig(ktrig) == 1) then + ;i34 0.0 .5 30000 7.00 .5 .7 1 1 1 1 1.5 .1 + idur random 1/12, 1/8 ; Duration + kdyn = kamp ; random 1/9, 1/2 ; Amplitude + kfqc random 500, 400 ; Pitch to frequency + krez random 1/2, .95 ; Tone + kspdec random 1, 2 ; Spring decay + kspton random 1, 2 ; Spring tone + kspmix random 1/9, 1 ; Spring mix + kspq random 1/4, 2 ; Spring Q + kpbnd random 1, 2 ; Pitch bend + kpbtm random 1/32, 1/8 ; Pitch bend time + schedulek "snare_impl", 0, idur, kdyn, kfqc, krez, kspdec, kspton, kspmix, kspq, kpbnd, kpbtm, ksav + endif +endop + + + + + + + + + + + + + + +gawater[] init 2 +instr water_verb + a1 = gawater[0] + a2 = gawater[1] + + aL, aR freeverb a1, a2, 0.8, 0.8, 48000 + aenv = linsegr:a(0, 1, 1, 1, 0) + aL *= aenv + aR *= aenv + aL = dcblock2(aL) + aR = dcblock2(aR) + outs aL*0.4, aR*0.4 + + gawater[0] = 0 + gawater[1] = 1 +endin +maxalloc("water_verb", 1) +schedule("water_verb", 0, -1) + +instr water_impl + + imaxshake = p4 + ifreq = p5 + ifreq1 = p6 + ifreq2 = p7 + + iamp init p8 + ipos init p9 + ;low amplitude + adrp dripwater .1, 0.09, 15, .9, imaxshake, ifreq, ifreq1, ifreq2 + asig clip adrp, 2, 0.9 ; avoid drips that drip too loud + ;asig = mirror(adrp, -0.9, 0.9) + asig *= (0.3 * iamp) + + a1, a2 pan2 asig, ipos + gawater[0] = gawater[0] + a1 + gawater[1] = gawater[1] + a2 + outs a1, a2 + +endin + +opcode water_drop, 0, kkk + kamp, kspeed, kshake xin + + kshake = limit:k(kshake, 0, 1) + ipos = random:i(0, 1) + kamp = limit:k(kamp, 0, 1) + kspeed = limit:k(kspeed, 0.001, 1)*15 + + kmet = drunk(kspeed, 0.7) + if(kmet == 1) then + kfq1 = random:k(380, 500) ;430 + kfq2 = random:k(900, 1200) ; 1000 + kfq3 = random:k(700, 880) ;800 + schedulek("water_impl", 0, 0.5, kshake, kfq1, kfq2, kfq3, kamp, ipos) + endif +endop + +instr ks_impl + ;ares pluck kamp, kcps, icps, ifn, imeth [, iparm1] [, iparm2] + inote init p4 + ;iamp init p5 + kamp init p5 + icps = mtof:i(inote) + kcps = icps + ifn init 0 + imeth init 1 + iparm1 = .001 + iparm2 = 10 + asig = pluck(kamp, kcps, icps, ifn, imeth, iparm1) + imod_div = rint:i(1, 4) * 2 + amod = pluck(1, kcps/imod_div, icps/imod_div, ifn, imeth, iparm1) + ares = asig * (a(0.5) - amod) + + kpregain = 2 + kpostgain = 0.25 + kshape1 = 0 + kshape2 = 0 + imode = 3 + ;adist distort1 ares, kpregain, kpostgain, kshape1, kshape2, imode + adist = pdclip(ares*10, .5, 0.6) + ablock = dcblock2(adist) + adist = balance(adist, ares) * kamp + + + ipan = random:i(0, 1) + al, ar pan2 ablock, ipan + chnmix(al, "out1") + chnmix(ar, "out2") +endin + +opcode strike, 0, kkkk + ktrig, kamp, knote, kdur xin + ktrig = limit:k(ktrig, 0, 1) + kamp = limit:k(kamp ,0, 1) + knote = limit:k(knote, 0, 100) + 20 + kdur = to_tempo_dur(limit:k(kdur, 0.001, 1000)) + if(check_trig(ktrig) == 1) then + schedulek("ks_impl", 0, kdur, knote, kamp) + endif +endop + + +instr vibish_impl + inote init p4 + iamp init p5 + + kadd1 = rspline:k(-0.45, 0.28, 0.2, 1) + kfq1 = mtof:k(inote+kadd1) + kadd2 = rspline:k(-0.45, 0.35, 0.3, 1) + kfq2 = mtof:k(inote+kadd2) + + iadd1 = random:i(.1, 6) + iadd2 = random:i(.1, 8) + + iprop1 = random:i(0.1, 0.9) + iprop1_inv = 1 - iprop1 + idest1 = random:i(0.3, 0.75) + kclipenv1 = cosseg:k(0, iprop1 * p3, idest1, iprop1_inv * p3, 0) + + iprop2 = random:i(0.1, 0.9) + iprop2_inv = 1 - iprop1 + idest2 = random:i(0.3, 0.75) + kclipenv2 = cosseg:k(0, iprop2 * p3, idest2, iprop2_inv * p3, 0) + + ao1 = pdclip(oscili(iamp, kfq2) + oscili(iamp, kfq1 + iadd1), kclipenv1, -0.5) + ao2 = pdclip(oscili(iamp, kfq2) + oscili(iamp, kfq2 + iadd2), kclipenv2, -0.5) + aenv = cossegr:a(0, p3/2, 1, p3/2, 0.05, p3/4, 0) + + ao1 *= aenv * iamp + ao2 *= aenv * iamp + chnmix(ao1, "out1") + chnmix(ao2, "out2") +endin + +opcode smooth, 0, kkkk + ktrig, kamp, knote, kdur xin + ktrig = limit:k(ktrig, 0, 1) + kamp = limit:k(kamp ,0, 1) + knote = limit:k(knote, 0, 100) + 20 + kdur = to_tempo_dur(limit:k(kdur, 0.001, 1000)) + if(check_trig(ktrig) == 1) then + schedulek("vibish_impl", 0, kdur, knote, kamp) + endif +endop + + +instr bounce_impl + inote init p4 + iamp init p5 + + ifq1 = mtof:i(inote) + random:i(-3, 2) + ifq2 = mtof:i(inote) + random:i(-3, 2) + ao1= vco2(iamp, ifq1) + ao2= vco2(iamp, ifq2) + + ao1 *= ao2 + ao2 *= ao1 + + kramp1 = expseg:k(random:i(0.1, 10), p3, random:i(0.1, 10)) + kramp2 = expseg:k(random:i(0.1, 10), p3, random:i(0.1, 10)) + kmod1 = pow(abs(lfo(0.3, kramp1, 5)), 5) + kmod2 = pow(abs(lfo(0.3, kramp2, 5)), 5) + + ao1 *= 1 - kmod1 + ao2 *= 1 - kmod2 + + aenv = cosseg:a(1, p3, 0) + + ao1 *= aenv + ao2 *= aenv + kphfq1 = rspline:k(100, 2000, .5, 3) + kphfq2 = rspline:k(200, 1800, .6, 3.3) + kphq = random:i(0.5, 0.9) + kmode init 1 + kord init 4 + ksep init 2 + kfb = 0.7 + + aph1 = phaser2(ao1, kphfq1, kphq, kord, kmode, ksep, kfb) * 0.4 + aph2 = phaser2(ao1, kphfq2, kphq, kord, kmode, ksep, kfb) * 0.4 + + chnmix(aph1*iamp, "out1") + chnmix(aph2*iamp, "out2") +endin + +opcode bounce, 0, kkkk + ktrig, kamp, knote, kdur xin + ktrig = limit:k(ktrig, 0, 1) + kamp = limit:k(kamp ,0, 1) + knote = limit:k(knote, 0, 100) + 20 + kdur = to_tempo_dur(limit:k(kdur, 0.001, 1000)) + if(check_trig(ktrig) == 1) then + schedulek("bounce_impl", 0, kdur, knote, kamp) + endif +endop + diff --git a/src/App.svelte b/src/App.svelte index 83c86de..be2db73 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -9,22 +9,15 @@ import AudioScope from "./lib/components/audio/AudioScope.svelte"; import Spectrogram from "./lib/components/audio/Spectrogram.svelte"; import ConfirmDialog from "./lib/components/ui/ConfirmDialog.svelte"; - import InputDialog from "./lib/components/ui/InputDialog.svelte"; - import TemplateDialog from "./lib/components/ui/TemplateDialog.svelte"; import CsoundReference from "./lib/components/reference/CsoundReference.svelte"; import { createCsoundDerivedStores, type LogEntry, type EvalSource, } from "./lib/csound"; - import { type CsoundProject } from "./lib/project-system"; - import { - templateRegistry, - type CsoundTemplate, - } from "./lib/templates/template-registry"; - import { loadLastProjectId } from "./lib/project-system/persistence"; + import { type File } from "./lib/project-system"; + import { loadOpenTabs, loadCurrentFileId } from "./lib/project-system/persistence"; import { createAppContext, setAppContext } from "./lib/contexts/app-context"; - import type { ProjectMode } from "./lib/project-system/types"; import { themes, applyTheme } from "./lib/themes"; import { Save, @@ -42,9 +35,9 @@ const { csound, - projectManager, + fileManager, editorSettings, - projectEditor, + editorState, uiState, executionContext, } = appContext; @@ -57,45 +50,39 @@ let logsUnsubscribe: (() => void) | undefined; onMount(async () => { - await projectManager.init(); + await fileManager.init(); + await editorState.refreshFileCache(); - const lastProjectId = loadLastProjectId(); - let projectToLoad: CsoundProject | null = null; + // Try to restore open tabs + const openTabIds = loadOpenTabs(); + const currentFileId = loadCurrentFileId(); - if (lastProjectId) { - const result = await projectManager.getProject(lastProjectId); - if (result.success) { - projectToLoad = result.data; + if (openTabIds.length > 0) { + // Restore tabs + for (const fileId of openTabIds) { + await editorState.openFile(fileId); } - } - if (!projectToLoad) { - const allProjectsResult = await projectManager.getAllProjects(); - if (allProjectsResult.success) { - if (allProjectsResult.data.length > 0) { - projectToLoad = allProjectsResult.data[0]; - } else { - const classicTemplate = templateRegistry.getById("classic"); - if (classicTemplate) { - const createResult = await projectManager.createProject({ - title: "Welcome", - author: "System", - content: classicTemplate.content, - tags: [], - mode: classicTemplate.mode, - }); - if (createResult.success) { - projectToLoad = createResult.data; - } - } + // Switch to last current file + if (currentFileId && openTabIds.includes(currentFileId)) { + await editorState.switchToFile(currentFileId); + } + } else { + // No tabs to restore, check if we have any files + const allFilesResult = await fileManager.getAllFiles(); + if (allFilesResult.success && allFilesResult.data.length > 0) { + // Open the first file + await editorState.openFile(allFilesResult.data[0].id); + } else { + // No files at all, create a welcome file + const welcomeContent = '\n\n-odac\n\n\n\nsr = 44100\nksmps = 32\nnchnls = 2\n0dbfs = 1\n\ninstr 1\n kEnv madsr 0.1, 0.2, 0.7, 0.5\n aOut oscil kEnv * 0.3, 440\n outs aOut, aOut\nendin\n\n\n\ni 1 0 2\n\n'; + const result = await fileManager.createFile({ title: 'Welcome.orc', content: welcomeContent }); + if (result.success) { + await editorState.openFile(result.data.id); } } } - if (projectToLoad) { - projectEditor.loadProject(projectToLoad); - } - logsUnsubscribe = csoundLogs.subscribe((logs) => { interpreterLogs = logs; }); @@ -134,94 +121,65 @@ } function handleEditorChange(value: string) { - projectEditor.setContent(value); + editorState.setContent(value); } - async function handleNewEmptyFile() { - const emptyTemplate = templateRegistry.getEmpty(); - const result = projectEditor.requestSwitch(async () => - await projectEditor.createNew(emptyTemplate.content, emptyTemplate.mode), - ); - - if (result === "confirm-unsaved") { - uiState.showUnsavedChangesDialog(); - } + async function handleNewFile() { + await editorState.createNewFile(); } - function handleNewFromTemplate() { - uiState.showTemplateDialog(); + async function handleFileOpen(fileId: string) { + await editorState.openFile(fileId); } - async function handleTemplateSelect(template: CsoundTemplate) { - uiState.hideTemplateDialog(); - - const result = projectEditor.requestSwitch(async () => - await projectEditor.createNew(template.content, template.mode), - ); - - if (result === "confirm-unsaved") { - uiState.showUnsavedChangesDialog(); - } + async function handleFileDelete(fileId: string) { + await editorState.deleteFile(fileId); } - function handleFileSelect(project: CsoundProject | null) { - if (!project) return; - - const result = projectEditor.requestSwitch(() => - projectEditor.loadProject(project), - ); - - if (result === "confirm-unsaved") { - uiState.showUnsavedChangesDialog(); - } + async function handleFileRename(fileId: string, newTitle: string) { + await editorState.renameFile(fileId, newTitle); } - async function handleExecute(code: string, source: EvalSource) { + async function handleTabClick(fileId: string) { + await editorState.switchToFile(fileId); + } + + async function handleTabClose(fileId: string) { + await editorState.closeFile(fileId); + } + + async function handleExecuteFile() { try { - await executionContext.execute(code, source); + await executionContext.executeFile(); + } catch (error) { + console.error("Execution error:", error); + } + } + + async function handleExecuteBlock(code: string) { + try { + await executionContext.executeBlock(code); + } catch (error) { + console.error("Execution error:", error); + } + } + + async function handleExecuteSelection(code: string) { + try { + await executionContext.executeSelection(code); } catch (error) { console.error("Execution error:", error); } } async function handleSave() { - await projectEditor.save(); - } - - async function handleSaveAs(title: string) { - const success = await projectEditor.saveAs(title); - if (success) { - uiState.hideSaveAsDialog(); - } - } - - async function handleMetadataUpdate( - projectId: string, - updates: { - title?: string; - author?: string; - mode?: import("./lib/project-system/types").ProjectMode; - }, - ) { - await projectEditor.updateMetadata(updates); - } - - async function handleSwitchSave() { - await projectEditor.confirmSaveAndSwitch(); - uiState.hideUnsavedChangesDialog(); - } - - function handleSwitchDiscard() { - projectEditor.confirmDiscardAndSwitch(); - uiState.hideUnsavedChangesDialog(); + await editorState.save(); } async function handleShare() { - if (!projectEditor.currentProjectId) return; + if (!editorState.currentFileId) return; - const result = await projectManager.exportProjectToUrl( - projectEditor.currentProjectId, - ); + const result = await fileManager.exportFilesToUrl([editorState.currentFileId]); if (result.success) { try { await navigator.clipboard.writeText(result.data); @@ -262,18 +220,7 @@ }); $effect(() => { - const mode = projectEditor.currentProject?.mode || "composition"; - const projectId = projectEditor.currentProjectId; - - if (mode !== executionContext.mode) { - executionContext.switchMode(mode).catch(console.error); - } - - executionContext.setContentProvider(() => projectEditor.content); - - if (projectId) { - executionContext.startSession(projectId).catch(console.error); - } + executionContext.setContentProvider(() => editorState.content); }); const panelTabs = [ @@ -301,12 +248,11 @@ {#snippet filesTabContent()} {/snippet} @@ -327,27 +273,27 @@ @@ -357,7 +303,7 @@ class="icon-button evaluate-button" disabled={!$initialized} class:is-running={$running} - title="Evaluate (Cmd-E)" + title="Evaluate Block (Cmd-E)" > @@ -417,12 +363,19 @@
@@ -520,29 +473,6 @@ {/snippet} - - - - - uiState.hideTemplateDialog()} - /> diff --git a/src/lib/components/ui/Tabs.svelte b/src/lib/components/ui/Tabs.svelte new file mode 100644 index 0000000..98179cf --- /dev/null +++ b/src/lib/components/ui/Tabs.svelte @@ -0,0 +1,121 @@ + + +
+ {#if files.length === 0} +
No files open
+ {:else} + {#each files as file} +
handleTabClick(file.id)} + > + {file.title} + {#if unsavedFileIds.has(file.id)} + + {/if} + +
+ {/each} + {/if} +
+ + diff --git a/src/lib/components/ui/TemplateDialog.svelte b/src/lib/components/ui/TemplateDialog.svelte deleted file mode 100644 index 3ec8f42..0000000 --- a/src/lib/components/ui/TemplateDialog.svelte +++ /dev/null @@ -1,129 +0,0 @@ - - -{#if visible} - -{/if} - - diff --git a/src/lib/contexts/app-context.ts b/src/lib/contexts/app-context.ts index 0806115..e4003c0 100644 --- a/src/lib/contexts/app-context.ts +++ b/src/lib/contexts/app-context.ts @@ -1,37 +1,37 @@ import { getContext, setContext } from 'svelte'; -import { ProjectManager } from '../project-system/project-manager'; -import { ProjectDatabase } from '../project-system/db'; +import { FileManager } from '../project-system/file-manager'; +import { FileDatabase } from '../project-system/db'; import { createCsoundStore } from '../csound/store'; import { ExecutionContext } from '../csound/execution-context'; import { createEditorSettingsStore } from '../stores/editorSettings'; -import { ProjectEditor } from '../stores/projectEditor.svelte'; +import { EditorState } from '../stores/editorState.svelte'; import { UIState } from '../stores/uiState.svelte'; import type { CsoundStore } from '../csound/store'; import type { EditorSettingsStore } from '../stores/editorSettings'; export interface AppContext { - projectManager: ProjectManager; + fileManager: FileManager; csound: CsoundStore; executionContext: ExecutionContext; editorSettings: EditorSettingsStore; - projectEditor: ProjectEditor; + editorState: EditorState; uiState: UIState; } const APP_CONTEXT_KEY = Symbol('app-context'); export function createAppContext(): AppContext { - const db = new ProjectDatabase(); - const projectManager = new ProjectManager(db); + const db = new FileDatabase(); + const fileManager = new FileManager(db); const csound = createCsoundStore(); - const executionContext = new ExecutionContext(csound, 'composition'); + const executionContext = new ExecutionContext(csound); return { - projectManager, + fileManager, csound, executionContext, editorSettings: createEditorSettingsStore(), - projectEditor: new ProjectEditor(projectManager), + editorState: new EditorState(fileManager), uiState: new UIState() }; } diff --git a/src/lib/csound/execution-context.ts b/src/lib/csound/execution-context.ts index 041b715..594b25d 100644 --- a/src/lib/csound/execution-context.ts +++ b/src/lib/csound/execution-context.ts @@ -1,102 +1,48 @@ import type { CsoundStore } from './store'; -import type { ProjectMode } from '../project-system/types'; -import { CompositionStrategy, LiveCodingStrategy, type ExecutionStrategy } from './execution-strategies'; -export type EvalSource = 'selection' | 'block' | 'document'; - -export interface ExecutionSession { - mode: ProjectMode; - projectId: string | null; - isActive: boolean; - startTime: Date; -} +export type EvalSource = 'selection' | 'block' | 'file'; export class ExecutionContext { - private session: ExecutionSession | null = null; - private strategy: ExecutionStrategy; - private currentMode: ProjectMode; private contentProvider: (() => string) | null = null; - constructor( - private csound: CsoundStore, - mode: ProjectMode - ) { - this.currentMode = mode; - this.strategy = this.createStrategyForMode(mode); - } - - get mode(): ProjectMode { - return this.currentMode; - } - - get activeSession(): ExecutionSession | null { - return this.session; - } + constructor(private csound: CsoundStore) {} setContentProvider(provider: () => string): void { this.contentProvider = provider; } - async startSession(projectId: string | null): Promise { - await this.endSession(); - - this.session = { - mode: this.currentMode, - projectId, - isActive: true, - startTime: new Date() - }; - - if (this.currentMode === 'livecoding') { - this.resetStrategy(); - } - } - - async endSession(): Promise { - if (!this.session?.isActive) return; - - if (this.currentMode === 'livecoding') { - await this.csound.stop(); - this.resetStrategy(); + /** + * Execute entire file + */ + async executeFile(): Promise { + const content = this.contentProvider?.() ?? ''; + if (!content.trim()) { + return; } - this.session = { ...this.session, isActive: false }; + await this.csound.stop(); + await this.csound.evaluateCode(content); } - async execute(code: string, source: EvalSource): Promise { - if (this.currentMode === 'livecoding' && (!this.session || !this.session.isActive)) { - await this.startSession(this.session?.projectId ?? null); + /** + * Execute code block (instrument, opcode, or paragraph) + */ + async executeBlock(code: string): Promise { + if (!code.trim()) { + return; } - const fullContent = this.contentProvider?.() ?? ''; - await this.strategy.execute(this.csound, code, fullContent, source); + await this.csound.evaluateCode(code); } - async switchMode(newMode: ProjectMode): Promise { - if (newMode === this.currentMode) return; - - await this.endSession(); - this.currentMode = newMode; - this.strategy = this.createStrategyForMode(newMode); - - if (newMode === 'composition') { - await this.csound.stop(); + /** + * Execute selected text + */ + async executeSelection(code: string): Promise { + if (!code.trim()) { + return; } - } - private createStrategyForMode(mode: ProjectMode): ExecutionStrategy { - return mode === 'livecoding' - ? new LiveCodingStrategy() - : new CompositionStrategy(); - } - - private resetStrategy(): void { - if (this.strategy instanceof LiveCodingStrategy) { - this.strategy.reset(); - } - } - - async destroy(): Promise { - await this.endSession(); + await this.csound.evaluateCode(code); } } diff --git a/src/lib/project-system/compression.ts b/src/lib/project-system/compression.ts index 57aa4fe..b84d09a 100644 --- a/src/lib/project-system/compression.ts +++ b/src/lib/project-system/compression.ts @@ -1,7 +1,7 @@ import pako from 'pako'; -import type { CsoundProject, CompressedProject } from './types'; +import type { File, CompressedFiles } from './types'; -const COMPRESSION_VERSION = 1; +const COMPRESSION_VERSION = 3; /** * Convert a string to a Uint8Array @@ -59,12 +59,12 @@ function base64ToUint8Array(base64: string): Uint8Array { } /** - * Compress a project to a base64 string for sharing + * Compress files to a base64 string for sharing */ -export function compressProject(project: CsoundProject): CompressedProject { +export function compressFiles(files: File[]): CompressedFiles { try { - // Convert project to JSON string - const jsonString = JSON.stringify(project); + // Convert files to JSON string + const jsonString = JSON.stringify(files); // Convert to Uint8Array const uint8Array = stringToUint8Array(jsonString); @@ -80,14 +80,14 @@ export function compressProject(project: CsoundProject): CompressedProject { version: COMPRESSION_VERSION, }; } catch (error) { - throw new Error(`Failed to compress project: ${error instanceof Error ? error.message : 'Unknown error'}`); + throw new Error(`Failed to compress files: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** - * Decompress a base64 string back to a project + * Decompress a base64 string back to files */ -export function decompressProject(compressed: CompressedProject): CsoundProject { +export function decompressFiles(compressed: CompressedFiles): File[] { try { // Check version compatibility if (compressed.version !== COMPRESSION_VERSION) { @@ -104,24 +104,24 @@ export function decompressProject(compressed: CompressedProject): CsoundProject const jsonString = uint8ArrayToString(decompressed); // Parse JSON - const project = JSON.parse(jsonString) as CsoundProject; + const files = JSON.parse(jsonString) as File[]; - // Validate that we have the required fields - if (!project.id || !project.title || !project.content === undefined) { - throw new Error('Invalid project data structure'); + // Validate that we have an array + if (!Array.isArray(files)) { + throw new Error('Invalid files data structure'); } - return project; + return files; } catch (error) { - throw new Error(`Failed to decompress project: ${error instanceof Error ? error.message : 'Unknown error'}`); + throw new Error(`Failed to decompress files: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** - * Create a shareable URL from a project + * Create a shareable URL from files */ -export function projectToShareUrl(project: CsoundProject, baseUrl: string = window.location.origin): string { - const compressed = compressProject(project); +export function filesToShareUrl(files: File[], baseUrl: string = window.location.origin): string { + const compressed = compressFiles(files); const params = new URLSearchParams({ v: compressed.version.toString(), d: compressed.data, @@ -131,33 +131,33 @@ export function projectToShareUrl(project: CsoundProject, baseUrl: string = wind } /** - * Extract a project from a URL + * Extract files from a URL */ -export function projectFromShareUrl(url: string): CsoundProject { +export function filesFromShareUrl(url: string): File[] { try { const urlObj = new URL(url); const params = urlObj.searchParams; - const version = parseInt(params.get('v') || '1', 10); + const version = parseInt(params.get('v') || '3', 10); const data = params.get('d'); if (!data) { - throw new Error('No project data found in URL'); + throw new Error('No files data found in URL'); } - const compressed: CompressedProject = { version, data }; - return decompressProject(compressed); + const compressed: CompressedFiles = { version, data }; + return decompressFiles(compressed); } catch (error) { - throw new Error(`Failed to parse project from URL: ${error instanceof Error ? error.message : 'Unknown error'}`); + throw new Error(`Failed to parse files from URL: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Calculate the approximate compression ratio */ -export function getCompressionRatio(project: CsoundProject): number { - const original = JSON.stringify(project); - const compressed = compressProject(project); +export function getCompressionRatio(files: File[]): number { + const original = JSON.stringify(files); + const compressed = compressFiles(files); const originalSize = new TextEncoder().encode(original).length; const compressedSize = base64ToUint8Array(compressed.data).length; diff --git a/src/lib/project-system/db.ts b/src/lib/project-system/db.ts index d6f6b96..2e3dc1b 100644 --- a/src/lib/project-system/db.ts +++ b/src/lib/project-system/db.ts @@ -1,13 +1,13 @@ -import type { CsoundProject } from './types'; +import type { File } from './types'; -const DB_NAME = 'csound-projects-db'; -const DB_VERSION = 1; -const STORE_NAME = 'projects'; +const DB_NAME = 'csound-files-db'; +const DB_VERSION = 3; +const STORE_NAME = 'files'; /** * Database wrapper for IndexedDB operations */ -class ProjectDatabase { +class FileDatabase { private db: IDBDatabase | null = null; private initPromise: Promise | null = null; @@ -38,18 +38,19 @@ class ProjectDatabase { request.onupgradeneeded = (event) => { const db = (event.target as IDBOpenDBRequest).result; - // Create object store if it doesn't exist + // Delete old stores if they exist + if (db.objectStoreNames.contains('projects')) { + db.deleteObjectStore('projects'); + } + if (db.objectStoreNames.contains('workspaces')) { + db.deleteObjectStore('workspaces'); + } + + // Create files store if it doesn't exist if (!db.objectStoreNames.contains(STORE_NAME)) { - const objectStore = db.createObjectStore(STORE_NAME, { + db.createObjectStore(STORE_NAME, { keyPath: 'id', }); - - // Create indexes for efficient querying - objectStore.createIndex('title', 'title', { unique: false }); - objectStore.createIndex('author', 'author', { unique: false }); - objectStore.createIndex('dateCreated', 'dateCreated', { unique: false }); - objectStore.createIndex('dateModified', 'dateModified', { unique: false }); - objectStore.createIndex('tags', 'tags', { unique: false, multiEntry: true }); } }; }); @@ -69,9 +70,9 @@ class ProjectDatabase { } /** - * Get a project by ID + * Get a file by ID */ - async get(id: string): Promise { + async get(id: string): Promise { const db = await this.ensureDb(); return new Promise((resolve, reject) => { @@ -84,15 +85,15 @@ class ProjectDatabase { }; request.onerror = () => { - reject(new Error(`Failed to get project: ${request.error?.message}`)); + reject(new Error(`Failed to get file: ${request.error?.message}`)); }; }); } /** - * Get all projects + * Get all files */ - async getAll(): Promise { + async getAll(): Promise { const db = await this.ensureDb(); return new Promise((resolve, reject) => { @@ -105,34 +106,34 @@ class ProjectDatabase { }; request.onerror = () => { - reject(new Error(`Failed to get all projects: ${request.error?.message}`)); + reject(new Error(`Failed to get all files: ${request.error?.message}`)); }; }); } /** - * Save or update a project + * Save or update a file */ - async put(project: CsoundProject): Promise { + async put(file: File): Promise { const db = await this.ensureDb(); return new Promise((resolve, reject) => { const transaction = db.transaction([STORE_NAME], 'readwrite'); const store = transaction.objectStore(STORE_NAME); - const request = store.put(project); + const request = store.put(file); request.onsuccess = () => { resolve(); }; request.onerror = () => { - reject(new Error(`Failed to save project: ${request.error?.message}`)); + reject(new Error(`Failed to save file: ${request.error?.message}`)); }; }); } /** - * Delete a project by ID + * Delete a file by ID */ async delete(id: string): Promise { const db = await this.ensureDb(); @@ -147,57 +148,13 @@ class ProjectDatabase { }; request.onerror = () => { - reject(new Error(`Failed to delete project: ${request.error?.message}`)); + reject(new Error(`Failed to delete file: ${request.error?.message}`)); }; }); } /** - * Search projects by tag - */ - async getByTag(tag: string): Promise { - const db = await this.ensureDb(); - - return new Promise((resolve, reject) => { - const transaction = db.transaction([STORE_NAME], 'readonly'); - const store = transaction.objectStore(STORE_NAME); - const index = store.index('tags'); - const request = index.getAll(tag); - - request.onsuccess = () => { - resolve(request.result || []); - }; - - request.onerror = () => { - reject(new Error(`Failed to get projects by tag: ${request.error?.message}`)); - }; - }); - } - - /** - * Search projects by author - */ - async getByAuthor(author: string): Promise { - const db = await this.ensureDb(); - - return new Promise((resolve, reject) => { - const transaction = db.transaction([STORE_NAME], 'readonly'); - const store = transaction.objectStore(STORE_NAME); - const index = store.index('author'); - const request = index.getAll(author); - - request.onsuccess = () => { - resolve(request.result || []); - }; - - request.onerror = () => { - reject(new Error(`Failed to get projects by author: ${request.error?.message}`)); - }; - }); - } - - /** - * Clear all projects (use with caution!) + * Clear all files (use with caution!) */ async clear(): Promise { const db = await this.ensureDb(); @@ -212,7 +169,7 @@ class ProjectDatabase { }; request.onerror = () => { - reject(new Error(`Failed to clear projects: ${request.error?.message}`)); + reject(new Error(`Failed to clear files: ${request.error?.message}`)); }; }); } @@ -229,4 +186,4 @@ class ProjectDatabase { } } -export { ProjectDatabase }; +export { FileDatabase }; diff --git a/src/lib/project-system/file-manager.ts b/src/lib/project-system/file-manager.ts new file mode 100644 index 0000000..2a65ad1 --- /dev/null +++ b/src/lib/project-system/file-manager.ts @@ -0,0 +1,316 @@ +import type { File, CreateFileData, Result } from './types'; +import type { FileDatabase } from './db'; +import { compressFiles, decompressFiles, filesToShareUrl, filesFromShareUrl } from './compression'; +import { loadSystemFiles, isSystemFileId, getSystemFile } from './system-files'; + +async function wrapResult(fn: () => Promise, errorMsg: string): Promise> { + try { + const data = await fn(); + return { success: true, data }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error : new Error(errorMsg), + }; + } +} + +/** + * Generate a unique ID + */ +function generateId(): string { + return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; +} + +type FileChangeListener = () => void; + +/** + * File Manager - Main API for managing files + */ +export class FileManager { + private db: FileDatabase; + private changeListeners: Set = new Set(); + + constructor(db: FileDatabase) { + this.db = db; + } + + /** + * Subscribe to file changes + */ + onFilesChanged(listener: FileChangeListener): () => void { + this.changeListeners.add(listener); + return () => this.changeListeners.delete(listener); + } + + /** + * Notify all listeners that files have changed + */ + private notifyChange(): void { + this.changeListeners.forEach(listener => listener()); + } + + /** + * Initialize the file manager (initializes database) + */ + async init(): Promise { + await this.db.init(); + } + + /** + * Create a new file + */ + async createFile(data: CreateFileData): Promise> { + try { + const file: File = { + id: generateId(), + title: data.title, + content: data.content || '', + }; + + await this.db.put(file); + this.notifyChange(); + + return { success: true, data: file }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error : new Error('Failed to create file'), + }; + } + } + + /** + * Get a file by ID (checks both user and system files) + */ + async getFile(id: string): Promise> { + try { + if (isSystemFileId(id)) { + const systemFile = getSystemFile(id); + if (systemFile) { + return { success: true, data: systemFile }; + } + } + + const file = await this.db.get(id); + + if (!file) { + return { + success: false, + error: new Error(`File not found: ${id}`), + }; + } + + return { success: true, data: file }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error : new Error('Failed to get file'), + }; + } + } + + /** + * Get all files (user files + system files) + */ + async getAllFiles(): Promise> { + try { + const userFiles = await this.db.getAll(); + const systemFiles = await loadSystemFiles(); + + return { + success: true, + data: [...systemFiles, ...userFiles], + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error : new Error('Failed to get files'), + }; + } + } + + /** + * Update a file (system files cannot be updated directly) + */ + async updateFile(id: string, updates: Partial>): Promise> { + try { + if (isSystemFileId(id)) { + return { + success: false, + error: new Error('Cannot update system files directly. Use "Save As" to create a copy.'), + }; + } + + const existingFile = await this.db.get(id); + + if (!existingFile) { + return { + success: false, + error: new Error(`File not found: ${id}`), + }; + } + + const updatedFile: File = { + ...existingFile, + ...(updates.title !== undefined && { title: updates.title }), + ...(updates.content !== undefined && { content: updates.content }), + }; + + await this.db.put(updatedFile); + this.notifyChange(); + + return { success: true, data: updatedFile }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error : new Error('Failed to update file'), + }; + } + } + + /** + * Delete a file (system files cannot be deleted) + */ + async deleteFile(id: string): Promise> { + if (isSystemFileId(id)) { + return { + success: false, + error: new Error('Cannot delete system files'), + }; + } + + const result = await wrapResult(() => this.db.delete(id), 'Failed to delete file'); + if (result.success) { + this.notifyChange(); + } + return result; + } + + /** + * Duplicate a file (works with both user and system files) + */ + async duplicateFile(id: string): Promise> { + try { + const result = await this.getFile(id); + + if (!result.success) { + return result; + } + + const originalFile = result.data; + const duplicatedFile: File = { + id: generateId(), + title: `${originalFile.title} (copy)`, + content: originalFile.content, + }; + + await this.db.put(duplicatedFile); + this.notifyChange(); + + return { success: true, data: duplicatedFile }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error : new Error('Failed to duplicate file'), + }; + } + } + + /** + * Generate unique untitled filename + */ + async generateUntitledFilename(): Promise { + const result = await this.getAllFiles(); + if (!result.success) { + return 'Untitled-1.orc'; + } + + const files = result.data; + const untitledPattern = /^Untitled-(\d+)\.orc$/; + let maxNum = 0; + + files.forEach(file => { + const match = file.title.match(untitledPattern); + if (match) { + const num = parseInt(match[1], 10); + if (num > maxNum) { + maxNum = num; + } + } + }); + + return `Untitled-${maxNum + 1}.orc`; + } + + /** + * Export files to a shareable URL + */ + async exportFilesToUrl(fileIds: string[], baseUrl?: string): Promise> { + try { + const files: File[] = []; + + for (const id of fileIds) { + const file = await this.db.get(id); + if (file) { + files.push(file); + } + } + + if (files.length === 0) { + return { + success: false, + error: new Error('No files to export'), + }; + } + + const url = filesToShareUrl(files, baseUrl); + + return { success: true, data: url }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error : new Error('Failed to export files'), + }; + } + } + + /** + * Import files from a URL + */ + async importFilesFromUrl(url: string): Promise> { + try { + const files = filesFromShareUrl(url); + + // Generate new IDs for imported files + const importedFiles = files.map(file => ({ + ...file, + id: generateId(), + })); + + for (const file of importedFiles) { + await this.db.put(file); + } + + this.notifyChange(); + + return { success: true, data: importedFiles }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error : new Error('Failed to import files'), + }; + } + } + + /** + * Clear all files (use with caution!) + */ + async clearAllFiles(): Promise> { + const result = await wrapResult(() => this.db.clear(), 'Failed to clear files'); + if (result.success) { + this.notifyChange(); + } + return result; + } +} diff --git a/src/lib/project-system/index.ts b/src/lib/project-system/index.ts index abbde27..93ca774 100644 --- a/src/lib/project-system/index.ts +++ b/src/lib/project-system/index.ts @@ -1,56 +1,40 @@ /** - * Csound Project Management System + * Csound File Management System * - * This module provides a complete project system for managing Csound code files + * This module provides a file system for managing Csound files * with browser-based storage (IndexedDB) and import/export functionality via * compressed URLs. - * - * @example - * ```typescript - * import { projectManager } from './lib/project-system'; - * - * // Initialize - * await projectManager.init(); - * - * // Create a new project - * const result = await projectManager.createProject({ - * title: 'My First Csound Project', - * author: 'John Doe', - * content: '...', - * tags: ['synth', 'experiment'] - * }); - * - * // Get all projects - * const projects = await projectManager.getAllProjects(); - * - * // Export to shareable URL - * const urlResult = await projectManager.exportProjectToUrl(result.data.id); - * - * // Import from URL - * const imported = await projectManager.importProjectFromUrl(url); - * ``` */ // Export types export type { - CsoundProject, - CreateProjectData, - UpdateProjectData, - CompressedProject, + File, + CreateFileData, + CompressedFiles, Result, } from './types'; // Export main API -export { ProjectManager } from './project-manager'; +export { FileManager } from './file-manager'; -// Export database (for advanced usage) -export { projectDb } from './db'; +// Export database +export { FileDatabase } from './db'; -// Export compression utilities (for advanced usage) +// Export compression utilities export { - compressProject, - decompressProject, - projectToShareUrl, - projectFromShareUrl, + compressFiles, + decompressFiles, + filesToShareUrl, + filesFromShareUrl, getCompressionRatio, } from './compression'; + +// Export persistence utilities +export { + saveOpenTabs, + loadOpenTabs, + clearOpenTabs, + saveCurrentFileId, + loadCurrentFileId, + clearCurrentFileId, +} from './persistence'; diff --git a/src/lib/project-system/persistence.ts b/src/lib/project-system/persistence.ts index e168694..1bf8b84 100644 --- a/src/lib/project-system/persistence.ts +++ b/src/lib/project-system/persistence.ts @@ -1,17 +1,39 @@ -const LAST_PROJECT_KEY = 'oldboy:lastProjectId'; +const OPEN_TABS_KEY = 'oldboy:openTabs'; +const CURRENT_FILE_KEY = 'oldboy:currentFileId'; -export function saveLastProjectId(projectId: string | null): void { - if (projectId === null) { - localStorage.removeItem(LAST_PROJECT_KEY); - } else { - localStorage.setItem(LAST_PROJECT_KEY, projectId); +export function saveOpenTabs(fileIds: string[]): void { + localStorage.setItem(OPEN_TABS_KEY, JSON.stringify(fileIds)); +} + +export function loadOpenTabs(): string[] { + const stored = localStorage.getItem(OPEN_TABS_KEY); + if (!stored) { + return []; + } + try { + const parsed = JSON.parse(stored); + return Array.isArray(parsed) ? parsed : []; + } catch { + return []; } } -export function loadLastProjectId(): string | null { - return localStorage.getItem(LAST_PROJECT_KEY); +export function clearOpenTabs(): void { + localStorage.removeItem(OPEN_TABS_KEY); } -export function clearLastProjectId(): void { - localStorage.removeItem(LAST_PROJECT_KEY); +export function saveCurrentFileId(fileId: string | null): void { + if (fileId === null) { + localStorage.removeItem(CURRENT_FILE_KEY); + } else { + localStorage.setItem(CURRENT_FILE_KEY, fileId); + } +} + +export function loadCurrentFileId(): string | null { + return localStorage.getItem(CURRENT_FILE_KEY); +} + +export function clearCurrentFileId(): void { + localStorage.removeItem(CURRENT_FILE_KEY); } diff --git a/src/lib/project-system/system-files.ts b/src/lib/project-system/system-files.ts new file mode 100644 index 0000000..6c50e4e --- /dev/null +++ b/src/lib/project-system/system-files.ts @@ -0,0 +1,68 @@ +import type { File } from './types'; + +const SYSTEM_FILES_DIR = '/system-files'; + +const SYSTEM_FILE_NAMES = [ + 'archives.orc', + 'Documentation.orc', + 'examples.orc', + 'globals.orc', + 'lib.orc', + 'livecode.orc', + 'scale.orc', + 'synth.orc', +] as const; + +let systemFilesCache: File[] | null = null; + +/** + * Load all system files from the public directory + */ +export async function loadSystemFiles(): Promise { + if (systemFilesCache) { + return systemFilesCache; + } + + const files: File[] = []; + + for (const fileName of SYSTEM_FILE_NAMES) { + try { + const response = await fetch(`${SYSTEM_FILES_DIR}/${fileName}`); + if (!response.ok) { + console.error(`Failed to load system file: ${fileName}`); + continue; + } + + const content = await response.text(); + files.push({ + id: `system:${fileName}`, + title: fileName, + content, + readonly: true, + system: true, + }); + } catch (error) { + console.error(`Error loading system file ${fileName}:`, error); + } + } + + systemFilesCache = files; + return files; +} + +/** + * Check if a file ID corresponds to a system file + */ +export function isSystemFileId(id: string): boolean { + return id.startsWith('system:'); +} + +/** + * Get a system file by ID + */ +export function getSystemFile(id: string): File | undefined { + if (!systemFilesCache) { + return undefined; + } + return systemFilesCache.find((file) => file.id === id); +} diff --git a/src/lib/project-system/types.ts b/src/lib/project-system/types.ts index 65966dd..e7a73a0 100644 --- a/src/lib/project-system/types.ts +++ b/src/lib/project-system/types.ts @@ -1,68 +1,36 @@ -export type ProjectMode = 'composition' | 'livecoding'; - /** - * Core data structure for a Csound project + * A single file */ -export interface CsoundProject { - /** Unique identifier for the project */ +export interface File { + /** Unique identifier for the file */ id: string; - /** User-defined project title */ + /** Filename (e.g., "kick.orc", "main.csd") */ title: string; - /** Project author name */ - author: string; - - /** Date when the project was created (ISO string) */ - dateCreated: string; - - /** Date when the project was last modified (ISO string) */ - dateModified: string; - - /** Number of times the project has been saved */ - saveCount: number; - /** The Csound code content */ content: string; - /** Optional tags for categorization */ - tags: string[]; + /** Whether this file is read-only (can be viewed/edited but requires "Save As") */ + readonly?: boolean; - /** Csound version used to create this project */ - csoundVersion: string; - - /** Execution mode: composition (full document) or livecoding (block evaluation) */ - mode: ProjectMode; + /** Whether this is a system file (cannot be deleted, always visible) */ + system?: boolean; } /** - * Data structure for creating a new project (omits auto-generated fields) + * Data structure for creating a new file */ -export interface CreateProjectData { +export interface CreateFileData { title: string; - author: string; content?: string; - tags?: string[]; - mode?: ProjectMode; } /** - * Data structure for updating an existing project + * Compressed files data for import/export via links */ -export interface UpdateProjectData { - id: string; - title?: string; - author?: string; - content?: string; - tags?: string[]; - mode?: ProjectMode; -} - -/** - * Compressed project data for import/export via links - */ -export interface CompressedProject { - /** Base64-encoded compressed project data */ +export interface CompressedFiles { + /** Base64-encoded compressed files data */ data: string; /** Version of the compression format (for future compatibility) */ diff --git a/src/lib/stores/editorState.svelte.ts b/src/lib/stores/editorState.svelte.ts new file mode 100644 index 0000000..8226b9b --- /dev/null +++ b/src/lib/stores/editorState.svelte.ts @@ -0,0 +1,248 @@ +import type { File, FileManager } from '../project-system'; +import { saveOpenTabs, saveCurrentFileId } from '../project-system/persistence'; + +interface EditorStateData { + openFileIds: string[]; + currentFileId: string | null; + unsavedChanges: Map; // fileId -> modified content + files: Map; // fileId -> file data +} + +export class EditorState { + private fileManager: FileManager; + + private state = $state({ + openFileIds: [], + currentFileId: null, + unsavedChanges: new Map(), + files: new Map(), + }); + + private pendingAction: (() => void) | null = null; + + constructor(fileManager: FileManager) { + this.fileManager = fileManager; + } + + get openFileIds() { + return this.state.openFileIds; + } + + get currentFileId() { + return this.state.currentFileId; + } + + get currentFile() { + if (!this.state.currentFileId) { + return null; + } + return this.state.files.get(this.state.currentFileId) ?? null; + } + + get content() { + if (!this.state.currentFileId) { + return ''; + } + // Return unsaved content if exists, otherwise file content + return this.state.unsavedChanges.get(this.state.currentFileId) ?? this.currentFile?.content ?? ''; + } + + get hasUnsavedChanges() { + if (!this.state.currentFileId) { + return false; + } + return this.state.unsavedChanges.has(this.state.currentFileId); + } + + get isCurrentFileReadonly() { + return this.currentFile?.readonly ?? false; + } + + get openFiles() { + return this.state.openFileIds + .map(id => this.state.files.get(id)) + .filter((f): f is File => f !== undefined); + } + + get unsavedChanges() { + return this.state.unsavedChanges; + } + + hasUnsavedChangesForFile(fileId: string): boolean { + return this.state.unsavedChanges.has(fileId); + } + + setContent(content: string) { + if (!this.state.currentFileId) { + return; + } + this.state.unsavedChanges.set(this.state.currentFileId, content); + } + + async openFile(fileId: string) { + // Load file if not in cache + if (!this.state.files.has(fileId)) { + const result = await this.fileManager.getFile(fileId); + if (!result.success) { + return false; + } + this.state.files.set(fileId, result.data); + } + + // Add to open tabs if not already open + if (!this.state.openFileIds.includes(fileId)) { + this.state.openFileIds.push(fileId); + saveOpenTabs(this.state.openFileIds); + } + + // Switch to this file + this.state.currentFileId = fileId; + saveCurrentFileId(fileId); + + return true; + } + + async closeFile(fileId: string) { + // Check for unsaved changes + if (this.state.unsavedChanges.has(fileId)) { + // TODO: prompt user? + // For now, just discard changes + } + + // Remove from open tabs + this.state.openFileIds = this.state.openFileIds.filter(id => id !== fileId); + saveOpenTabs(this.state.openFileIds); + + // Clear unsaved changes + this.state.unsavedChanges.delete(fileId); + + // If this was the current file, switch to another + if (this.state.currentFileId === fileId) { + if (this.state.openFileIds.length > 0) { + this.state.currentFileId = this.state.openFileIds[0]; + saveCurrentFileId(this.state.currentFileId); + } else { + this.state.currentFileId = null; + saveCurrentFileId(null); + } + } + } + + async switchToFile(fileId: string) { + if (!this.state.openFileIds.includes(fileId)) { + return await this.openFile(fileId); + } + + this.state.currentFileId = fileId; + saveCurrentFileId(fileId); + return true; + } + + async save() { + if (!this.state.currentFileId) { + return false; + } + + const content = this.state.unsavedChanges.get(this.state.currentFileId); + if (content === undefined) { + return true; // Nothing to save + } + + const result = await this.fileManager.updateFile(this.state.currentFileId, { content }); + if (result.success) { + // Update file cache + this.state.files.set(this.state.currentFileId, result.data); + // Clear unsaved changes + this.state.unsavedChanges.delete(this.state.currentFileId); + return true; + } + + return false; + } + + async saveFile(fileId: string) { + const content = this.state.unsavedChanges.get(fileId); + if (content === undefined) { + return true; // Nothing to save + } + + const result = await this.fileManager.updateFile(fileId, { content }); + if (result.success) { + this.state.files.set(fileId, result.data); + this.state.unsavedChanges.delete(fileId); + return true; + } + + return false; + } + + async createNewFile() { + const title = await this.fileManager.generateUntitledFilename(); + const result = await this.fileManager.createFile({ title, content: '' }); + + if (result.success) { + this.state.files.set(result.data.id, result.data); + await this.openFile(result.data.id); + return true; + } + + return false; + } + + async renameFile(fileId: string, newTitle: string) { + const result = await this.fileManager.updateFile(fileId, { title: newTitle }); + if (result.success) { + this.state.files.set(fileId, result.data); + return true; + } + return false; + } + + async deleteFile(fileId: string) { + const result = await this.fileManager.deleteFile(fileId); + if (result.success) { + this.state.files.delete(fileId); + await this.closeFile(fileId); + return true; + } + return false; + } + + async refreshFileCache() { + const result = await this.fileManager.getAllFiles(); + if (result.success) { + this.state.files.clear(); + result.data.forEach(file => { + this.state.files.set(file.id, file); + }); + } + } + + requestSwitch(action: () => void): 'proceed' | 'confirm-unsaved' { + if (!this.hasUnsavedChanges) { + action(); + return 'proceed'; + } + + this.pendingAction = action; + return 'confirm-unsaved'; + } + + async confirmSaveAndSwitch(): Promise { + await this.save(); + this.pendingAction?.(); + this.pendingAction = null; + } + + confirmDiscardAndSwitch(): void { + if (this.state.currentFileId) { + this.state.unsavedChanges.delete(this.state.currentFileId); + } + this.pendingAction?.(); + this.pendingAction = null; + } + + cancelSwitch(): void { + this.pendingAction = null; + } +} diff --git a/src/lib/stores/uiState.svelte.ts b/src/lib/stores/uiState.svelte.ts index 4cbe04a..1b0398f 100644 --- a/src/lib/stores/uiState.svelte.ts +++ b/src/lib/stores/uiState.svelte.ts @@ -9,8 +9,6 @@ export class UIState { sharePopupVisible = $state(false); audioPermissionPopupVisible = $state(true); unsavedChangesDialogVisible = $state(false); - saveAsDialogVisible = $state(false); - templateDialogVisible = $state(false); shareUrl = $state(''); @@ -58,20 +56,4 @@ export class UIState { hideUnsavedChangesDialog() { this.unsavedChangesDialogVisible = false; } - - showSaveAsDialog() { - this.saveAsDialogVisible = true; - } - - hideSaveAsDialog() { - this.saveAsDialogVisible = false; - } - - showTemplateDialog() { - this.templateDialogVisible = true; - } - - hideTemplateDialog() { - this.templateDialogVisible = false; - } } diff --git a/src/lib/templates/template-registry.ts b/src/lib/templates/template-registry.ts deleted file mode 100644 index 9ac04b2..0000000 --- a/src/lib/templates/template-registry.ts +++ /dev/null @@ -1,162 +0,0 @@ -import type { ProjectMode } from '../project-system/types'; - -export interface CsoundTemplate { - id: string; - name: string; - mode: ProjectMode; - content: string; -} - -const EMPTY_TEMPLATE: CsoundTemplate = { - id: 'empty', - name: 'Empty', - mode: 'composition', - content: ` - --odac - - - -sr = 48000 -ksmps = 32 -nchnls = 2 -0dbfs = 1 - - - - - - -` -}; - -const CLASSIC_TEMPLATE: CsoundTemplate = { - id: 'classic', - name: 'Classic', - mode: 'composition', - content: ` - --odac - - - -sr = 48000 -ksmps = 32 -nchnls = 2 -0dbfs = 1 - -instr 1 - iFreq = p4 - iAmp = p5 - - kEnv madsr 0.01, 0.1, 0.6, 0.2 - - aOsc oscili iAmp * kEnv, iFreq - - outs aOsc, aOsc -endin - - - -i 1 0.0 0.5 261.63 0.3 -i 1 0.5 0.5 329.63 0.3 -i 1 1.0 0.5 392.00 0.3 -i 1 1.5 0.5 523.25 0.3 - - -` -}; - -const LIVECODING_TEMPLATE: CsoundTemplate = { - id: 'livecoding', - name: 'Live Coding', - mode: 'livecoding', - content: `gaReverb init 0 - -instr 1 - kFreq chnget "freq" - kFreq = (kFreq == 0 ? p4 : kFreq) - kAmp = p5 - kEnv linsegr 0, 0.01, 1, 0.1, 0.7, 0.2, 0 - aOsc vco2 kAmp * kEnv, kFreq - aFilt moogladder aOsc, 2000, 0.3 - outs aFilt, aFilt - gaReverb = gaReverb + aFilt * 0.3 -endin - -instr 2 - iFreq = p4 - iAmp = p5 - kEnv linsegr 0, 0.005, 1, 0.05, 0.5, 0.1, 0 - aOsc vco2 iAmp * kEnv, iFreq, 10 - aFilt butterlp aOsc, 800 - outs aFilt, aFilt -endin - -instr 99 - aL, aR freeverb gaReverb, gaReverb, 0.8, 0.5 - outs aL, aR - gaReverb = 0 -endin - -; Start reverb (always on) -i 99 0 -1 - - -i 1 0 2 440 0.3 - -; Arpeggio -i 1 0 0.5 261.63 0.2 -i 1 0.5 0.5 329.63 0.2 -i 1 1.0 0.5 392.00 0.2 -i 1 1.5 0.5 523.25 0.2 - -; Bass line -i 2 0 0.5 130.81 0.4 -i 2 0.5 0.5 146.83 0.4 -i 2 1.0 0.5 164.81 0.4 -i 2 1.5 0.5 130.81 0.4 - -; Long note for channel control -i 1 0 10 440 0.3 - -freq = 440 - -freq = 554.37 - -freq = 659.25 - -; Turn off instrument 1 -i -1 0 0 -` -}; - -const TEMPLATE_REGISTRY: CsoundTemplate[] = [ - EMPTY_TEMPLATE, - LIVECODING_TEMPLATE, - CLASSIC_TEMPLATE -]; - -export class TemplateRegistry { - private templates: Map; - - constructor() { - this.templates = new Map( - TEMPLATE_REGISTRY.map(template => [template.id, template]) - ); - } - - getAll(): CsoundTemplate[] { - return Array.from(this.templates.values()); - } - - getById(id: string): CsoundTemplate | undefined { - return this.templates.get(id); - } - - getEmpty(): CsoundTemplate { - return EMPTY_TEMPLATE; - } -} - -export const templateRegistry = new TemplateRegistry();