Try   HackMD

FoxDot Introduction

=====================

Part 1: Les bases

Pour exécuter le code dans FoxDot, placez votre curseur texte sur la ligne de code et appuyez sur Ctrl+Enter ou Cmd+Enter sous MacOS.

Pour commencer à faire du son dans FoxDot, nous attribuons à un "Player" un instrument numérique utilisant :

p1

p1 est le nom de notre "player", et les flèches (>>) sont utilisées pour lui donner des instructions.
La première instruction est "commencez à jouer de l'instrument 'pads'". Ces instruments sont appelés "SynthDefs" - pour voir la liste des SynthDefs disponibles

print(SynthDefs)

Pour l'instant, nous nous contenterons d'utiliser l'instrument "pads".
La plupart des instructions sont placées entre parenthèses, mais certaines actions sont effectuées à l'aide de fonctions.
Par exemple, pour arrêter un player, nous "appelons" la fonction d'arrêt en tapant le nom, puis un point, suivi du nom de l'action et ensuite entre parenthèses.
Les players ont tous des noms à deux caractères - si nous utilisons un nom à trois caractères, nous obtenons une erreur

foo >> pads()

Pour modifier le comportement de "p1", nous pouvons lui donner des instructions entre parenthèses :

p1 >> pads() p1 >> pads(2) p1 >> pads([0,2,4]) p1.stop()

La première instruction est la tonalité des notes - il peut s'agir d'une valeur unique ou d'une liste de valeurs entre crochets.
0 fait référence à la première note de la gamme
Si nous utilisons des parenthèses rondes au lieu de parenthèses carrées, cela permet de jouer les notes ensemble

p1 >> pads((0,2,4)) p1 >> pads([0,1,2,(3,5,7)]) p1.stop()

Le fait de placer une liste à l'intérieur d'une autre liste alterne la valeur utilisée

p1 >> pads([0,2,[4,7]]) p1.stop()

Après avoir défini la tonalité, nous utilisons des arguments pour donner d'autres instructions - comme les durées

p1 >> pads([0,1,2,3], dur=1) p1 >> pads([0,1,2,3], dur=1/2) p1 >> pads([0,1,2,3], dur=[1,1/2]) p1.stop()
print(Player.get_attributes()

Les arguments utiles sont "amp", "dur", "sus", "pan", "oct"
la liste de l'ensemble des arguments

p1 >> pads([0,1,2,3], dur=[1,1/2,1/2], amp=[1.5,0.5], sus=2, pan=[-1,1], oct=[5,5,5,(4,6)])

Jouons avec des sons de percussions. Pour déclencher les samples, nous utilisons un instrument spécial appelé "play".
et au lieu de donner une liste de tons à jouer, nous lui donnons une chaîne de caractères entre guillemets

d1 >> play("x-o-") d1.stop()

Chaque caractère est associé à un dossier de samples. "x" est une grosse caisse, "-" est un charley et "o" est une caisse claire.
Pour sélectionner un fichier différent dans le dossier, nous utilisons le mot-clé "sample" :

d1 >> play("x-o-", sample=1) d1 >> play("x-o-", sample=2) d1 >> play("x-o-", sample=[0,1,2]) d1.stop()

Il peut aussi s'agir d'une liste de numéros ! Nous pouvons rendre notre motif de percussions plus complexe en utilisant des crochets

d1 >> play("x-o[--]") d1 >> play("x-o(-o)") d1 >> play("x-o{-o*}") d1.stop()

Les crochets jouent plusieurs samples dans l'espace d'un temps :

d1 >> play("x-o[--]") d1 >> play("x-o[---]") d1 >> play("x-o[-------]") d1.stop()

Les parenthèses rondes alternent le son utilisé :

d1 >> play("x-o(-o)") d1 >> play("x-o(-[-o])") d1 >> play("x-o(-[-(-o)])") d1.stop()

Et Les parenthèses bouclées {} sélectionnent un sample au hasard pour plus de variété

d1 >> play("x-o{-o}") d1 >> play("x-o{-[--]o}") d1.stop()

Tout comme avant, nous pouvons utiliser les arguments :

d1 >> play("x-(-[-o])", dur=[3/4,3/4,1/2], pan=[-1,1])

Vous pouvez également modifier la vitesse de lecture de l'audio

d1 >> play("x-(-[-o])", dur=[3/4,3/4,1/2], pan=[-1,1], rate=1) d1 >> play("x-(-[-o])", dur=[3/4,3/4,1/2], pan=[-1,1], rate=2) d1 >> play("x-(-[-o])", dur=[3/4,3/4,1/2], pan=[-1,1], rate=0.5) d1 >> play("x-(-[-o])", dur=[3/4,3/4,1/2], pan=[-1,1], rate=-1) d1 >> play("x-(-[-o])", dur=[3/4,3/4,1/2], pan=[-1,1], rate=[1,2,0.5,-1]) d1.stop()

Pour jouer des choses en même temps, il suffit d'utiliser plusieurs players.

p1 >> pads([0,2,4,9,7], dur=1/4) d1 >> play("x-x-") d2 >> play(" * ")

Pour tout arrêter, appuyez sur "Cmd+." ou lancez la ligne de code ci-dessous.

Clock.clear()

Part 2: Composer un morceau

Les gammes ! Jusqu'à présent, nous n'avons utilisé que la majeure qui est la valeur par défaut, mais nous pouvons en utiliser tout un tas et même composer la nôtre !
Pour voir la liste des gammes, utilisez :

print(Scale.names())

Utilisons la gamme "dorian" !

Scale.default = "dorian"

Tout le timing est géré par "Clock" et nous pouvons changer le tempo si nous le voulons

Clock.bpm = 144

Commençons par un rythme de base du drum

d1 >> play("x ")

Ajouter une ligne de basse

b1 >> sawbass([0,-1,3], dur=[4,4,8])

Ajoutons quelques accords - nous pouvons nous assurer qu'ils s'accordent avec la basse en réglant la hauteur relative à la hauteur de la basse, b1, en utilisant l'opérateur d'addition (+).

p1 >> star(b1.pitch) p1 >> star(b1.pitch + (0,2,4)) p1 >> star(b1.pitch + (0,2,4), dur=[3/4, 3/4, 1/2])

Créez votre propre mélodie pour l'accompagner - lancez un SynthDef

print(SynthDefs) # par exemple blip p2 >> blip([0,7,6,4,2], dur=1/2, sus=1)

Ajoutons des percussions.

d1 >> play("x-") d2 >> play(" * ")

Qu'arrive-t-il à nos accords si nous changeons la basse ?

b1 >> sawbass([2,3,4,6], dur=4)

Part 3: Algorithmic music

Parfois, nous voulons donner des instructions aux players qui n'arrivent pas tout de suite. On peut utiliser la fonction "every" pour dire "tous les 8 temps, fais qqchose". Par exemple :

p1 >> pads([0,1,2,3,4,5,6,7]) p1.every(8, "reverse") p1.stop()

Tous les 8 temps, le player commence à jouer les notes dans l'ordre décroissant puis change à nouveau tous les 8 temps.
On peut aussi "mélanger" l'ordre des notes !

p1 >> pads([0,1,2,3,4,5,6,7]) p1.every(8, "shuffle") p1.stop()

Au lieu de tout mettre sur une nouvelle ligne, il est possible de "chaîner" ces fonctions ensemble :

p1 >> pads([0,1,2,3]).every(4, "reverse") p1 >> pads([0,1,2,3]).every(4, "reverse").stop()

Une autre fonction intéressante est le "stutter" ("bégaiement"), qui joue de multiples sons. Après "stutter", nous pouvons fournir plus d'arguments entre parenthèses comme nous l'avons fait avec le SynthDef :

d1 >> play("x-o-").every(4, "stutter") d1 >> play("x-o-").every(4, "stutter", 4) d1 >> play("x-o-").every(4, "stutter", 16)

Nous pouvons utiliser des mots-clés comme nous l'avons fait ci-dessus avec "stutter", pour changer la façon dont la fonction fonctionne

d1 >> play("x-o-").every(4, "stutter", 4, pan=[-1,1])

d1 >> play("x-o-").every(4, "stutter", 4, dur=3)

d1.stop()

Pour les players "mélodiques" (où nous jouons des notes), nous pouvons utiliser une fonction sympa appelée "offadd" qui jouera une nouvelle note sur le décalage, quelques ton plus haut que l'original

Tous les 3 temps, jouez une note sur le temps mort qui est 4

x1 >> blip([0,2,3,4]).every(3, "offadd", 4)

Vous pouvez "enchaîner" plus d'une fonction répétée,

x1 >> blip([0,2,3,4]).every(3, "offadd", 4).every(7, "stutter", 4)

Essayez de changer certains des mots-clés

x1 >> blip([0,2,3,4]).every(3, "offadd", 4).every(7, "stutter", 4, dur=3, pan=[-1,1], oct=6)

Enchaînez autant de fonctions différentes que vous le souhaitez !

x1 >> blip([0,2,3,4]).every(3, "offadd", 4).every(7, "stutter", 4, dur=3, pan=[-1,1], oct=5).every(8, "reverse")

Part 4: Ajout et enchaînement des effets

Nous pouvons aussi ajouter des effets aux players et les séquencer comme nous l'avons fait avec les durées et les intensités. Nous pouvons ajouter une réverbération en spécifiant la taille de la pièce et le pourcentage de la réverb

p1 >> blip(dur=4, sus=1, room=1, mix=0.5) p1 >> blip(dur=1, sus=1, room=[0,0.25,0.5,1], mix=[0, 0.1, 0.3, 0.5]) p1.stop()

Vous trouverez ci-dessous d'autres effets à essayer.
vous pouvez les appliquer à d'autres SynthDefs
Voici la liste et le détails de chacun

print(FxList)

High Pass Filter - laisse seulement les fréquences supérieures à cette valeur dans le signal

d1 >> play("x-o-") d1 >> play("x-o-", hpf=500) d1 >> play("x-o-", hpf=5000) d1 >> play("x-o-", hpf=[0,100,250,500,1000,2000,4000,8000]) d1.stop()

Low pass filter - ne laisse que les fréquences en dessous de cette valeur dans le signal

d1 >> play("x-o-") d1 >> play("x-o-", lpf=5000) d1 >> play("x-o-", lpf=500) d1 >> play("x-o-", lpf=[50,100,200,400,800,1600,3200,6400]) d1.stop()

Chop - découpe le signal en "x" parties

p1 >> pluck([0,4], dur=4, chop=4) p1 >> pluck([0,4], dur=4, chop=8) p1 >> pluck([0,4], dur=4, chop=320) p1.stop()

Que se passe-t-il lorsque vous utilisez un sustain "sus" différent de la durée ?

p1 >> pluck([0,4], dur=[3/4,3/4,1/2], chop=4) p1 >> pluck([0,4], dur=[3/4,3/4,1/2], chop=4, sus=2) p1.stop()

Shape - distorsion de la forme des ondes

b1 >> bass(dur=8) b1 >> bass(dur=8, shape=0.5) b1 >> bass(dur=8, shape=[0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0])

Part 5: Les Patterns

Python utilise des "listes" pour stocker de multiples données mais elles ne sont pas souvent manipulées d'une manière qui soit utile pour créer de la musique intéressante.,C'est pourquoi FoxDot utilise le type de données appelé Pattern qui est une extension des "listes"
Pour créer une Pattern simple, il suffit de mettre un "P" majuscule devant une liste

l = [0, 1, 2, 3] p = P[0, 1, 2, 3] print(l, type(l)) print(p, type(p))

Maintenant, nous pouvons faire des choses intéressantes avec le "p" que nous ne pouvons pas faire avec le "l".
Par exemple, si nous voulons doubler toutes les valeurs de "l", le moyen le plus court est de le faire de cette manière :

print([x * 2 for x in l])

C'est un peu lourd, surtout par rapport à la façon dont nous le faisons avec les patterns :

print(p * 2)

Nous pouvons faire toutes les opérations arithmétiques que nous voulons, et même utiliser des listes/patterns :

print(P[0, 1, 2, 3] * [1, 4])

Les listes sont également accompagnées de toute une série de fonctions utiles pour manipuler l'ordre, etc. Voci la liste

print(p.reverse()) print(p.rotate()) print(p.palindrome()) print(p.stretch(6))

Nous pouvons les chaîner ensemble :

print(p.reverse().palindrome().rotate() * 2)

Vous pouvez même "remplir automatiquement" un pattern avec des chiffres si vous vous sentez paresseux.
C'est très similaire à la fonction 'range' en Python où vous spécifiez le début,la fin, et l'orde des nombres que vous voulez :

print(P[0:10]) print(P[10:0:-1]) print(P[0:10:2].shuffle())

Pour plus d'informations sur les méthodes Pattern, consultez

help(Pattern)

Réalisons quelques exemples plus utiles puis voyons comment nous pouvons les utiliser :
Pour joindre deux ou plusieurs patterns ensemble, utilisez le symbole de la "barre | " de cette façon : On obtient 0-4 en série, puis dans un ordre aléatoire

print(P[:4] | P[:4].shuffle())

Pour créer des groupes de valeurs, comme des accords, dans les Patterns, vous pouvez utiliser le symbole &.
pour regouper deux patterns en un seul groupe de patterns - ne vous inquiétez pas s'ils sont de tailles différentes

print(P[:4] & P[9, 11])

Pour obtenir un sous-ensemble d'un pattern, nous pouvons utiliser l'indexation Python normale.
Par exemple, si vous voulez juste un élément d'un pattern, nous mettons des crochets à la fin du pattern et le numéro de l'article que nous voulons

evens = P[2, 4, 6, 8] print(evens[2])

Vous obtenez 6 pour l'index 2, car les index commencent à 0 !

Pour obtenir un sous-ensemble, vous pouvez utiliser la méthode que nous avons fait pour générer une série !

print(evens[0:3])

Nous obtenons les trois premiers éléments de la liste

Même si l'éventail dépasse la fin du pattern, nous obtiendrons la taille que nous avons demandée

print(evens[0:6])

Créons une mélodie soignée en utilisant un pattern

p1 >> blip(P[:10:2][:8].rotate(-3), dur=1/2, sus=2)

Combinez avec un pattern "PStep"

p1 >> blip(P[:10:2][:8].rotate(-3) + PStep(8,[0,3]).rotate(), dur=1/2, sus=2) p1.stop()

Alors, qu'est-ce que le PStep ?
Eh bien, il existe dans FoxDot des fonctions qui permettent de générer des patterns
pour vous permettre d'économiser de la frappe. Examinons quelques modèles utiles :

print(PStep(5,4,0)) # Pattern de longueur 5, se terminant par un 4, rempli de 0 print(PDur(3,8)) # Rythme euclidien, 3 pulsations sur 8 temps print(PSine(8)) # valeur n représentant une onde sinusoïdale

Mettons-les en pratique

p1 >> blip(P[:10:2][:8].rotate(-3) + PStep(8,[0,3]).rotate() | P[[9,11]], dur=PDur(3,8), sus=2, pan=PSine(16).shuffle()) d1 >> play("Xs") b1 >> dbass([0, 2, 3, 4], dur=8) Clock.clear()

Les fonctions de Pattern, telles que "palindrome" et "trim", peuvent être appelées régulièrement, tout comme les fonctions de Player comme "stutter" pour créer des modèles qui changent au fil du temps !

p1 >> blip(P[:10:2], dur=PDur(5,8)*2, sus=2, oct=PStep(7,5,6)).every(6, "reverse").often("trim", 3).every(9, "stutter", 4, dur=3) d1 >> play("Xs") b1 >> dbass([0, 2, 3, 4], dur=8) Clock.clear()

Il existe aussi des patterns "infinis" qui continuent à générer des valeurs pour vous. Ils ont très utile lorsqu'on utilise des éléments de hasard.

Générer des entiers aléatoires entre 0 et 8 en utilisant "PRand".

p1 >> blip(PRand(0, 8)) p1.stop()

Générer des valeurs aléatoires à partir d'une liste

p1 >> blip(PRand([0, 2, 4])) p1.stop()

Générer des valeurs flottantes dans une palette en utilisant PWhite

p1 >> blip(pan=PWhite(-1, 1), amp=PWhite(0, 1)) p1.stop()

Un bon exemple de combinaison d'indexation et de valeurs aléatoires pour créer des rythmes intéressants :

d1 >> play("x-o ") p1 >> blip(dur=1/4, amp=PRand([0,1])[:8]) Clock.clear()

Part 6: Utiliser le temps

Jusqu'à présent, nous avons utilisé des listes de numéros pour des choses comme les tonalités et les durées mais que faire si nous voulons que les valeurs changent en fonction du temps plutôt que de la séquence ?
Voici un exemple :

Utilisons une basse pour jouer ces notes pendant 8 temps chacune

b1 >> bass([0, 3], dur=8)

Et si nous voulons changer la durée mais continuer à jouer les notes pendant 8 temps chacun ?

b1 >> bass([0, 3], dur=PDur(3,8)) b1.stop()

Et bien ça ne marche pas. C'est pour ça que nous utilisons un outil appelé "var" qui signifie "variable" et qui varie en fonction du rythme :

b1 >> bass(var([0, 3], dur=8), dur=PDur(3,8)) b1.stop()

Il a joué chaque tonalité pendant 8 temps, même si les durées étaient inférieures à 8.
On crée un "var" en utilisant le code - var(Liste de valeurs, dur=duration)

b1 >> bass(var([0, 3], dur=8), dur=PDur(3,8))

Remarquez la différence entre changer la durée dans la basse et la durée dans le var

b1 >> bass(var([0, 3], dur=8), dur=PDur(5,8)) b1 >> bass(var([0, 3], dur=[6, 2]), dur=PDur(5,8)) b1.stop()

Ils sont particulièrement utiles pour créer des séquences d'accords. Utilisons-les dans notre morceau de tout à l'heure :
Voici notre basse pour commencer :

b1 >> sawbass([0,-1,3], dur=[4,4,8])

Créons un var appelé "chords" pour que nous puissions l'utiliser dans plusieurs players :

var.chords = var([0,-1,3], dur=[4,4,8]) b1 >> sawbass(var.chords, dur=1)

Nous pouvons maintenant changer la durée comme nous voulons

b1 >> sawbass(var.chords, dur=PDur(3,8)*2)

et d'autres instructions pour créer des patterns intéressants !

b1 >> sawbass(var.chords, dur=PDur(3,8)*2, oct=[5,5,[6,4],5], pan=[0,[-1,1]]) + [0,0,4,0,7]

Nous pouvons utiliser la variable "var.chords" comme une seule note dans une liste :

p1 >> blip([var.chords,2,3,4], sus=2)

Écoutez la première note de la séquence, elle change avec les accords. On peut continuer d'ajouter toutes sortes de fonctions à la séquence pour la rendre plus intéressante

p1 >> blip([var.chords,2,3,4], sus=2).every(3, "offadd", 4) p1 >> blip([var.chords,2,3,4], sus=2).every(3, "offadd", 4).every(7, "stutter", 4, dur=3, pan=[-1,1], oct=6) p1 >> blip([var.chords,2,3,4], sus=2).every(3, "offadd", 4).every(7, "stutter", 4, dur=3, pan=[-1,1], oct=6).every(8, "reverse")

Cette fois, nous utiliserons plutôt "var.chords" que "b1.pitch" lors de l'ajout (0, 2, 4).

p2 >> star(var.chords + (0, 2, 4), dur=PDur(3,8))

Que se passe-t-il si nous utilisons b1.pitch ?

p2 >> star(b1.pitch + (0, 2, 4), dur=PDur(3,8))

Introduisons à nouveau des percussions !

d1 >> play("x-") d2 >> play(" H ")

Utilisons un nouveau "var" pour appliquer un filtre passe-haut sur les drums pour la dernière mesure d'un cycle de 8 mesures
Réglez le filtre sur 0 pour 28 temps (7 mesures x 4 temps) et ensuite sur 8000 pour 4 temps (1 mesure x 4 temps)

var.filter = var([0,8000], dur=[28,4]) d1 >> play("x-", hpf=var.filter) d2 >> play(" H ", hpf=var.filter)

Essayez d'ajouter vos éléments - voyez s'il y a d'autres façons intéressantes de combiner la variable "var.chords".

Essayez d'appliquer le "var.filter" à p1, p2 et b1.
Essayez d'utiliser des crochets et .every pour rendre les sons de percussion plus complexes
Que se passe-t-il lorsque vous changez "var.chords" ?

var.chords = var([2,3,4,6], dur=4)

Part 7: PGroups et patterns complexes

Jusqu'à présent, nous avons fait référence à des chiffres entre parenthèses comme celui-ci : (0, 2, 4) comme des groupes de valeurs.
FoxDot les traite comme un type de données appelé "PGroup" qui est un type de pattern. Regardez le code ci-dessous

print(P[0, 1, 2, (3, 4)])

Nous obtenons P[0, 1, 2, P(3, 4)] - le dernier élément est un PGroup. Comme avec les patterns, nous pouvons simplement mettre un "P" devant les parenthèses pour convertir en PGroup et faire des trucs sympas avec

print(P(0, 1, 2, 3, 4).reverse()) print(P(0, 1, 2, 3, 4).shuffle())

En plus de jouer des notes ensemble comme ça :

p1 >> pads(P(0, 2, 4, 6), dur=4) p1.stop()

Les PGroups peuvent également être utilisés pour jouer des notes en succession rapide en mettant des symboles différents entre le "P" et les parenthèses :

p1 >> pads(P(0, 2, 4, 6), dur=4) p1 >> pads(P*(0, 2, 4, 6), dur=4) p1 >> pads(P*(0, 2, 4, 6), dur=2) p1.stop()

Vous remarquez la différence ?
Le P* joue toutes les notes sur toute la durée de la note, tout comme les crochets dans le synthétiseur "play".
Vous pouvez utiliser le symbole + pour répartir les notes sur le sustain d'une note par opposition à la durée, ce qui peut être utile lorsque vous utilisez des durées variables :

p1 >> pluck(P*(0, 4), dur=PDur(3,8), sus=1) p1 >> pluck(P+(0, 4), dur=PDur(3,8), sus=1) p1 >> pluck(P+(0, 4), dur=PDur(3,8), sus=2) p1.stop()

Nous utilisons un de ces PGroups lorsque nous utilisons la fonction "offadd" avec un Pattern
qui nous permet de spécifier les durées entre les notes en utilisant le symbole ^ et la durée comme dernière valeur du groupe :

print(P[0, 1, 2, 3].offadd(4)) p1 >> pluck(P^(0, 2, 4, 0.5), dur=4) # retard de 0,5 battement p1 >> pluck(P^(0, 2, 4, 0.75), dur=2) # retard de 0,75 battement p1 >> pluck([P*(0, 3), P^(0, 2, 4, 0.75)], dur=2) # Combinaison de plusieurs PGroups p1.stop()

Donc P[:4].offadd(4) est la même chose que P[:4] + P^(0, 4, 0.5) !

p1 >> pluck(P[:4].offadd(4)) p1 >> pluck(P[:4] + (P^(0,4,0.5))) p1.stop()

Ils sont utiles pour créer des variations de rythme sans avoir à créer un argument "dur" complexe. par exemple pour créer la mélodie simple ci-dessous :

p1 >> pluck([[0, P*(0, 0)], 2, -3, 2])

Sinon nous devrions faire ce-ci :

p1 >> pluck([0, 2, -3, 2, 0, 0, 2, -3, 2], dur=P[1,1/2,1].stutter([4,2,3])) p1.stop()

Part 8: Séquences de samples avancés

Créons un rythme de percussion très simple

d1 >> play("xs", sample=2) d2 >> play(" * ", sample=2) d3 >> play("(+ )+( [( +)+])", sample=2) Clock.clear()

Nous avons dû utiliser trois players différents pour ce qui n'est qu'une séquence de percussion.
Si cela peut faciliter le changement d'arguments, comme la durée ou l'amplitude de certaines parties de la séquence, il peut aussi être utile de traiter la séquence dans son ensemble.
Nous pouvons "superposer" plusieurs chaînes d'échantillons en plaçant les séquences les unes à côté des autres mais en les entourant de flèches <> comme cela :

d1 >> play("<xs>< * ><(+ )+( [( +)+])>", sample=2) d1.stop()

Assurez-vous qu'il n'y a pas d'espaces entre les différents modèles !
Vous pouvez également utiliser ces parenthèses pour jouer des samples ensemble dans une séquence, comme celle-ci :

d1 >> play("x-<*><o>-")

Bien qu'il soit parfois plus facile de les diviser en séquences séparées :

d1 >> play("<x-*->< o >") d1.stop()

Les séquences n'ont pas besoin d'être de la même longueur, alors essayez plusieurs séquences de longueurs différentes !

Vous pouvez considérer chaque séquence comme des couches superposées qui sont regroupées à l'aide de PGroups.
Si vous souhaitez modifier les arguments ou les effets d'une couche individuelle, nous devons utiliser des PGroups pour le faire.
Ainsi, si nous avons 3 couches et que nous voulons fixer le sample de la première couche à 2 mais les autres à 0, nous pourrions utiliser un code qui ressemble à ceci :

d1 >> play("<xs>< * ><(+ )+( [( +)+])>", sample=P(2, 0, 0)) d1.stop()

De même, si je veux régler le panoramique stéréo de la dernière couche pour alterner à gauche puis à droite (c'est-à-dire -1 et 1),
alors je pourrais utiliser un code qui ressemble à ceci :

d1 >> play("<xs>< * ><(+ )+( [( +)+])>", sample=P(2, 0, 0), pan=P(0, 0, [-1, 1])) d1.stop()

Tout cela est très utile quand on veut utiliser "every" avec une séquence plus complexe :

d1 >> play("<xs>< * ><(+ )+( [( +)+])>", sample=P(2, 0, 0), pan=P(0, 0, [-1, 1])).every([12,4], "trim", 3) d1.stop()

Lorsque vous créez une séquence complexe, vous pourriez vouloir utiliser un sample spécifique avec une valeur séparée au reste de la séquence.
Par exemple, si vous voulez que le sample "*" soit 2 mais que les autres soient 0, vous pourriez le faire de cette façon :

d1 >> play("x-*-", sample=[0,0,2,0]) d1.stop()

Mais que se passe-t-il lorsque vous changez la séquence ?

d1 >> play("x-*(-[-*])", sample=[0,0,2,0])

Vous devez ensuite mettre à jour les arguments du sample en fonction de cela :

d1 >> play("x-*(-[-*])", sample=[0,0,2,[0,(0,2)]]) d1.stop()

C'est un peu ennuyeux. Vous pouvez en fait spécifier le numéro du sample pour un caractère spécifique en le mettant entre les barres || avec le numéro que vous voulez :

d1 >> play("x-|*2|-") d1 >> play("x-|*2|(-[-|*2|])") d1.stop()

Vous pouvez également utiliser des séquences / patterns de cette manière !

d1 >> play("x-o|n[01]|") d1 >> play("x-|o(02)|-") d1 >> play("x-|o(0[20])|-") d1 >> play("x-o|a{0123}|") d1.stop()

Part 9: Utilisation de samples longs (fichiers wav)

Pour pouvoir utiliser un fichier wav long, il faut le placer dans un répertoire spécifique :

  1. dans FoxDot, ouvrir "Help&Settings">"Open Samples Folder":

  2. Placer le fichier wav dans le sous-répertoire "loop":

Ensuite, pour "jouer" ce fichier wav dans FoxDot , il faut utiliser le player 'loop".
Ici 'dur=1' précise que l'on veut un extrait d'une durée d'un temps musical du fichier wav (ici appelé 'foxdot.wav'). Rappelons qu'avec le bpm à 120, un temps musical (beat) vaut 0.5 seconde.

s1 >> loop('foxdot',dur=1)

Nous pouvons aussi indiquer une liste ‘[2,1]’ qui précise les emplacements des extraits (dont la durée reste fixée dans l’attribut ‘dur’) que l’on souhaite lire. Le premier élément ‘2’ précise que l’on va commencer par lire l’extrait d’un temps musical qui démarre au 3ième temps musical dans le fichier wav (notons que les indices de liste démarrent à 0, et donc 2 fait référence au 3ième temps). Le deuxième élément ‘1’ précise que l’on va lire l’extrait (toujours d’une durée fixée dans l’attribut ‘dur’) qui démarre au 2ième temps musical dans le fichier wav.

s1 >> loop('foxdot',[2,1],dur=1)

Evidemment nous pouvons faire de cette liste une Pattern et lui appliquer toutes les méthodes habituelles : https://foxdot.org/docs/pattern-methods/

s1 >> loop('foxdot',P[2,3,8,1].stutter(3),dur=1)

Si nous voulons lire la totalité du sample, il ne suffit pas d’augmenter la valeur de l’attribut ‘dur’ pour qu’elle coresponde à la durée du sample.
La valeur de l’attribut ‘dur’ correspond à la durée de la boucle ‘loop’. Donc chaque modification de la ligne devra attendre la fin de la boucle pour être prise en compte. Ce ci pose naturellement des problèmes de réactivité pour l’aplication d’effets sur un sample de plusieurs secondes ou minutes.
Pour remédier à ce problème nous allons découper le sample en une suite de boucles qui s’enchaînent les unes après les autres.
Sur le même principe que celui de la liste nous allons enchainer les emplacements d’extraits (dont la durée est fixée dans l’attribut ‘dur’) de 0 (départ) jusqu’à la fin du sample. Ici le sample ‘foxdot’ dure 5 secondes, nous avons vu qu’avec un bpm à 120, un temps musical (beat) vaut 0.5 seconde, il nous faut donc 10 temps musicaux.

print(P[:11]) P[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] s1 >> loop('foxdot',P[:11],dur=1)

Nous pouvons également y préciser le point de départ du sample, ici le temps ‘3’

print(P[3:11]) P[3, 4, 5, 6, 7, 8, 9, 10] s1 >> loop('foxdot',P[3:11],dur=1)

Si vous voulez lancer un sample long quand vous le souhaitez et être certain qu’il démarre bien au début, vous devez insérer votre liste dans une var, puis indiquer le nombre de fois où chaque valeur de la liste sera jouée, ici ‘1’ (une fois) et enfin lui indiquer à quel moment la liste doit démarrer, ici ‘ start=nextbar’.

s1 >> loop('foxdot', var(P[:11],1,start=nextbar))

Nous pouvons aussi lui indiquer de s’arreter à la fin du sample c’est à dire à la fin de la liste pour qu’il soit lu qu’une seule fois. Ici la valeur ‘11’ placé dans ‘after()’ correspond au dernier temps musical

s1 >> loop('foxdot', var(P[:11],1,start=nextbar)).after(11, "stop")

Part 10 : Séquencer l'exécution d'instructions FoxDot

On illustre ici comment préparer des séquences d'instruction qui vont être exécutées selon un échéancier choisi.
Il y a deux phases principales : la déclaration des séquences, l'échéancement des séquences.

Déclaration des séquences

Voici un exemple :

def sequence(f): f() return f @sequence def sequence1(): s1>>play("g", sample=2,rate=[1,2]) @sequence def sequence2(): s1.pitch="ggGG" s2 >> sinepad(P[2,4,3,0]+var([0,2],8), tremolo=[2,4], dur=[1,1/2]) @sequence def sequence3(): s3 >> prayerbell([2,6],dur=var([1/4,1],[1,3]),sus=0.1) s2.amp= var([1,0.5],4) Clock.clear()

a) Les trois premières lignes sont des instructions Python à copier tel quel (pour les programmeurs: déclaration d'une fonction 'sequence' dont on va modifier le comportement à l'aide d'un décorateur).

b) On déclare ensuite ci-dessus 3 séquences (on peut en faire autant que l'on veut). La première séquence correspond au code

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

@sequence def sequence1(): s1>>play("g", sample=2,rate=[1,2])

Ce qu'il faut en retenir:

  • il faut commencer par une ligne avec @sequence (pour les programmeurs: on déclare un décorateur de la fonction 'sequence')
  • il faut ensuite une ligne pour déclarer le nom de la séquence, ici appelée 'sequence1' (pour les programmeurs : c'est le nom de la fonction décorant la fonction 'sequence'). Cette ligne doit commencer par 'def' et se terminer avec ':', et on doit ajouter '()' collé au nom choisi pour la séquence.
  • puis on met des lignes FoxDot en les indentant (une tabulation pour débuter la ligne). Dans cet exemple, il y a une seule ligne FoxDot qui déclare le player "s1" de type "play", mais on peut mettre autant de lignes (toutes indentées) que nécessaires

Notons que dans la deuxième séquence (appelée sequence2) et que je prévois être exécutée après sequence1, je modifie un attribut d'un player qui a été lancé dans sequence1 et je démarre un nouveau player de type sinepad:

def sequence2(): s1.pitch="ggGG" s2 >> sinepad(P[2,4,3,0]+var([0,2],8), tremolo=[2,4], dur=[1,1/2])

c) On termine la déclaration des séquences par la ligne

Clock.clear()

d) Une fois la déclaration des séquences saisies sur FoxDot, il faut la charger en mémoire : il faut faire interpréter toutes ces lignes d'un coup (utiliser typiquement Ctrl + Return).

Echéancement des séquences

Une fois que l'on a chargé en mémoire les séquences (étapes d) ci-dessus), on peut échéancer l'exécution de ces séquences. Par exemple:

start = Clock.mod(4) - 0.1 Clock.schedule(sequence1, start) Clock.schedule(sequence2, start + 8) Clock.schedule(sequence3, start + 16)

Ces lignes doivent être interprétées dans FoxDot en même temps (typiquement avec un Ctrl + Return).
Ce qu'il faut en retenir :

  • la première ligne récupère l'instant de démarrage de la prochaine mesure (dans 4 temps musicaux) et le stocke dans la variable 'start'
  • la deuxième ligne planifie l'exécution de la première séquence (appelée 'sequence1') au début de la prochaine mesure (c'est-à-dire à l'instant stocké dans la variable 'start')
  • la troisème ligne planifie l'exécution de la deuxième séquence (appelée 'sequence2') 8 temps musicaux après le début de la prochaine mesure (c'est-à-dire à l'instant 'start + 8')
  • la quatrième ligne planifie l'exécution de la troisième séquence (appelée 'sequence3') 16 temps musicaux après le début de la prochaine mesure

Part 11 : Construire Accords & Mélodies

Les gammes

Une règle simple à retenir pour construire vos accords et mélodies avec FoxDot

La gamme détermine les notes contenues dans les accords et les mélodies utilisés.

Pour connaitre toutes les gammes proposées dans Foxdot

print(Scale.names())

Pour connaitre les notes contenues dans une gamme, ici la gamme chromatic

print(Scale.chromatic) P[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
0 1 2 3 4 5 6 7 8 9 10 11
Do Do# Ré# Mi Fa Fa# Sol Sol# La La# si

Structure des touches :

  • Les touches blanches sont les notes naturelles : Do, Ré, Mi, Fa, Sol, La, Si.
  • Les touches noires sont les notes avec des altérations (dièse ou bémol) : (Do#) ou (Ré♭), (Ré#) ou (Mi♭), (Pas de touche noire entre Mi et Fa), (Fa#) ou (Sol♭), (Sol#) ou (La♭), (La#) ou (Si♭), (Pas de touche noire entre Si et Do).

Il faut savoir que toutes les notes de la gamme chromatic sont séparées d’un demi ton chacune.

À noter que les dièses et les bémols sont les mêmes notes mais changent de nom en fonction de la note qui les précède.
Le dièse (noté #) réhausse la note qu'il précède d'un demi-ton.
Le bémol (noté b) abaisse la note qu'il précède d'un demi-ton.
C’est à dire que Do# est précédé du Do mais le Do# devient un Reb si il est précédé d’un Re.

Voilà l’ensemble des gammes proposées dans FoxDot avec leurs notes associées.

Étape 1 : Qu'est-ce qu'une Scale (gamme) ?

Une "scale" ou gamme est un ensemble organisé de notes que l'on utilise pour composer ou improviser de la musique. Imaginez cela comme un nuancier musical : chaque gamme propose une série de notes qui s'harmonisent entre elles et qui créent un certain "mood". Une gamme est définie par l'espacement entre ses notes.

Exemple : La gamme la plus courante est la gamme majeure. Elle commence par une note de base (la tonique) et suit une structure spécifique pour atteindre l’octave (la même note que celle de départ, mais plus haute).

Étape 2 : Structure d'une gamme

Une gamme n'est pas simplement une liste de notes au hasard. Elle suit un schéma précis de "tons" et de "demi-tons" (écart entre deux notes). Voici la structure de deux gammes courantes :

Gamme majeure (par exemple en Do majeur : Do - Ré - Mi - Fa - Sol - La - Si - Do) : ton, ton, demi-ton, ton, ton, ton, demi-ton.
Gamme mineure naturelle (par exemple en Do mineur : Do, Ré, Mi♭, Fa, Sol, La♭, Si♭, Do) : ton, demi-ton, ton, ton, demi-ton, ton, ton.

Ton : Deux touches sur un clavier (par exemple, de Do à Ré).
Demi-ton : Une touche sur un clavier (par exemple, de Mi à Fa).

Étape 3 : Interpréter ces listes de notes

Dans FoxDot et Renardo, les notes sont généralement interprétées comme des "degrés" dans une gamme plutôt que des notes spécifiques. Cela signifie que vous pouvez choisir une gamme (par exemple, majeure ou mineure) et FoxDot/Renardo se chargera de jouer les notes correspondantes.

Exemple : p1 >> pluck([0,1,2,3,4,5,6], scale=Scale.minor)

Ici, les nombres 0,1,2,3,4,5,6 sont des "degrés" de la gamme mineure. Ils ne représentent pas directement des notes spécifiques, mais FoxDot/Renardo les convertira en notes de la gamme mineure que vous avez choisie. Si votre base est "Do", alors ces degrés joueront Do, Ré, Mi♭, Fa, Sol, La♭, Si♭. Ces notes suivent bien le schéma : ton - demi-ton - ton - ton - demi-ton - ton - ton, de la gamme mineure naturelle.

Règle de la gamme mineure naturelle :
ton : distance de 2 demi-tons (deux touches sur un piano, par exemple de Do à Ré).
demi-ton : distance d'un demi-ton (une touche sur un piano, par exemple de Mi à Fa).

Pour la gamme Do mineur naturel, en partant de la tonique Do, voici les intervalles entre les notes, appliqués successivement selon le schéma :

  1. Do → Ré (ton, 2 demi-tons)
  2. Ré → Mi♭ (demi-ton, 1 demi-ton)
  3. Mi♭ → Fa (ton, 2 demi-tons)
  4. Fa → Sol (ton, 2 demi-tons)
  5. Sol → La♭ (demi-ton, 1 demi-ton)
  6. La♭ → Si♭ (ton, 2 demi-tons)
  7. Si♭ → Do (ton, 2 demi-tons)
Exemple : pluck([0,2,4,5,7], scale=Scale.minor)

Les chiffres 0, 2, 4, 5, 7 représentent effectivement des degrés ou positions dans la gamme mineure que tu as choisie. Voici le détail :

  • 0 correspond à la première note de la gamme (tonique).
  • 2 correspond à la troisième note de la gamme.(tierce)
  • 4 correspond à la cinquième note de la gamme.(quinte)
  • 5 correspond à la sixième note de la gamme.
  • 7 correspond à la huitième note (ou la même note que la première, mais une octave plus haute).

Il existe beaucoup de gammes, par exemple :

Gamme pentatonique (5 notes)
majeure : ton, ton, 1,5 ton, ton, 1,5 ton
mineure : 1,5 ton, ton, ton, 1,5 ton, ton
Gamme hexatonique (6 notes)
1,5 ton, ton, demi-ton, demi-ton, 1,5 ton, ton
Gammes heptatonique (7 notes)
majeure : ton, ton, demi-ton, ton, ton, ton, demi-ton.
mineure : ton, demi-ton, ton, ton, demi-ton, ton, ton.
Gamme octotonique (8 notes)
(demi-ton/ton) : demi-ton, ton, demi-ton, ton, demi-ton, ton, demi-ton, ton
(ton/demi-ton) : ton, demi-ton, ton, demi-ton, ton, demi-ton, ton, demi-ton
Gamme chromatique : demi-ton entre chaque note

0 1 2 3 4 5 6 7 8 9 10 11
Do Do# Ré# Mi Fa Fa# Sol Sol# La La# si

Les notions de "majeur" et "mineur" sont spécifiques aux gammes diatoniques, qui sont basées sur un schéma précis de tons et de demi-tons. Ces gammes ont des relations harmoniques très claires constituées par la 1ère, 3ème, et 5ème notes de la gamme :

Gamme majeure : (1)tonique (la fondamentale), (3)tierce majeure, (5)quinte juste
Gamme mineure : (1)tonique (la fondamentale), (3)tierce mineure, (5)quinte juste

Les accords parfaits majeurs ou mineurs sont directement dérivés des gammes et sont les accords les plus fondamentaux en harmonie tonale. Chaque degré d'une gamme peut théoriquement donner un accord, mais le 1er degré (la fondamentale), le 3ème degré (la tierce), et le 5ème degré (la quinte) constituent l'accord parfait de cette gamme, aussi appelé accord tonique.

Pour changer la tonique :

Tu peux utiliser le paramètre root pour choisir une nouvelle note de base. Par exemple, si tu veux que la tonique soit (La), tu fais :

python

p1 >> pluck([0, 2, 4, 5, 7], scale=Scale.minor, root=9)

Par défaut, root=0 et signifie que la tonique est (Do). Si tu veux changer cette tonique :

  • root=0 correspond à (Do)
  • root=2 correspond à (Ré)
  • root=4 correspond à (Mi)
  • root=5 correspond à (Fa)
  • root=7 correspond à (Sol)
  • root=9 correspond à (La)

Exemple avec la gamme blues et une tonique différente :

Si tu veux jouer une gamme blues avec G (Sol) comme tonique, tu peux faire :

python

p1 >> pluck([0, 3, 5, 6, 7, 10], scale=Scale.blues, root=7)

Ici, root=7 signifie que la tonique de la gamme est G (Sol). Les degrés de la gamme seront donc transposés à partir de cette note.

Les accords

On désigne par « accord » la combinaison d’au moins trois notes différentes, d'une même gamme, jouées simultanément. les accords permettent de créer la partie accompagnement.

Chacune des trois notes de l’accord possède un nom qui lui est propre :
• La première note de l’accord s’appelle la fondamentale
• La deuxième note de l’accord s’appelle la tierce
• Et la troisième note de l’accord s’appelle la quinte
la fondamentale donne son nom à l’accord. Par exemple si la fondamentale de votre accord est un Do, alors vous êtes en présence d’un accord de Do, si c’est un La, alors vous êtes en présence d’un accord de La, etc.

Chaque note de l’accord doit être séparée par un intervalle de tierce (c’est-à-dire un intervalle de trois notes).

Exemples:
Un accord de Do majeur : Do/Mi/Sol : deux tons / un ton et demi
Un accord de La mineur : La/Do/Mi : un ton et demi / deux tons
Pour les deux accords : 1ere note (la fondamentale), 3ème note (la tierce), et la 5ème note (la quinte)

L’ intervalle de tierce peut être majeur ou mineur

Une tierce majeure sera composée de deux tons : exemple Do/Mi/Sol
Une tierce mineure sera composée d’un ton et demi : exemple La/Do/Mi

Je commence par choisir une gamme, par exemple

print(Scale.aeolian) P[0, 2, 3, 5, 7, 8, 10]
0 1 2 3 4 5 6 7 8 9 10 11
Do Do# Ré# Mi Fa Fa# Sol Sol# La La# Si

Ainsi je dispose de ces notes [0, 2, 3, 5, 7, 8, 9, 11] pour composer mes accords et mes lignes mélodiques.
ATTENTION
Lorsque vous composez avec une gamme vous ne devez pas utiliser les valeurs de la liste données par print(Scale) qui définissent les notes de la gamme. Vous devez utiliser les valeurs qui définissent l'ordre des notes dans la gamme.
Commençons par un accord qui suit la règle de l’intervalle de tierce.

Scale.default = Scale.aeolian m1 >> piano(P[(0,2,4)],dur=1,oct=4)

Ensuite nous pouvons lui ajouter une ligne mélodique qui utilisent les notes proposées dans la même gamme. En jouant sur la durée et l’octave des notes pour apporter du rythme. ( None est une note silencieuse pour créer une pause dans la ligne)

Scale.default = Scale.aeolian m1 >> pbass(P[(0,2,4),None,(0,2,4),(0,2,4)],dur=[1,3,1,1],oct=[4], sus=1, root=[0,2,3,2]) m2 >> sawbass([0,2,5,8,5,None,5,8,5,8,7,8,5,8,11,8,5,None],dur=0.25,oct=4) m3 >> play ("x.x.Xs",sample= [0,2])