Ένας βασικός λόγος που τα ηχητικά παραδείγματα των προηγούμενων κεφαλαίων δεν έχουν πολύ ενδιαφέρον, είναι ότι οι κυματομορφές που έχουμε παραγάγει παραμένουν εντελώς σταθερές. Η συχνότητα και η ένταση του ήχου δεν μεταβάλλονται καθώς προχωράει ο χρόνος και αυτή η απόλυτη στατικότητα στο φάσμα συνήθως δεν είναι ελκυστική. Στον κόσμο γύρω μας τα ηχητικά γεγονότα γεννιούνται, μεταβάλλονται, εξασθενούν και πεθαίνουν. Το φάσμα των ήχων αλλάζει συν τω χρόνω. Η απόλυτη επανάληψη είναι κάτι ξένο στη φύση. Στην προκειμένη περίπτωση αυτό που ενδιαφέρει είναι να καταφέρουμε να κατασκευάσουμε χρονικά μεταβαλλόμενες κυματομορφές ώστε η σύνθεση ήχου που κάνουμε να αρχίσει να αποκτά σταδιακά κάποια μουσική ποιότητα.
Το επόμενο γράφημα δείχνει συμβολικά τη λειτουργία του ημιτονοειδούς ταλαντωτή στο SuperCollider, όπως την ξέρεις. Η γεννήτρια ήχου σαρώνει επαναλαμβανόμενα έναν πίνακα κυματομορφής (wavetable) στον οποίο βρίσκεται αποθηκευμένη η συνάρτηση του ημίτονου. Όταν τα τέσσερα ορίσματα που δέχεται το SinOsc
(freq, phase, mul, add) παραμένουν αμετάβλητα τότε δημιουργείται μία στατική κυματομορφή η οποία δεν είναι πολύ χρήσιμη αν θέλεις να φτιάξεις μουσική.
Σκέψου για παράδειγμα ένα κομμάτι μουσικής που σου αρέσει. Πιθανότατα αποτελείται από διακριτά ηχητικά γεγονότα που έχουν μπει στη σειρά το ένα μετά το άλλο και που μπορείς εύκολα να αναγνωρίσεις, όπως π.χ. οι ξεχωριστές νότες μίας κιθάρας, τα χτυπήματα ενός κρουστού, κ.λπ. Συνήθως κάτι τέτοιο θα θέλαμε να προσομοιώσουμε και όταν συνθέτουμε ήχους.
Για να δημιουργήσουμε μεμονωμένα ηχητικά γεγονότα συγκεκριμένης διάρκειας μπορούμε να χρησιμοποιήσουμε μία καμπύλη ώστε να μεταβάλουμε το πλάτος της ταλάντωσης κατά τη διάρκεια του χρόνου. Μία τέτοια καμπύλη ονομάζεται envelope (περιβάλλουσα) και δεν είναι τίποτα παραπάνω από ένα σχήμα το οποίο μπορούμε να αντιστοιχίσουμε σε οποιαδήποτε παράμετρο θέλουμε να μεταβάλλουμε χρονικά, όπως είναι π.χ. το πλάτος (amplitude envelope) ή η συχνότητα (frequency envelope).
Οι περιβάλλουσες είναι πολύ χρήσιμα πράγματα στη σύνθεση. Μας δείχνουν τη μεταβολή της ενέργειας μιας παραμέτρου. Υπάρχουν πολύ απλά και αρκετά πολύπλοκα σχήματα που μπορούν να χρησιμοποιηθούν. Η πιο εύκολη περίπτωση είναι η ευθεία γραμμή. Στο SuperCollider το αντικείμενο Line
παρέχει τη δυνατότητα να συνδέσουμε γραμμικά δύο τιμές σε συγκεκριμένο χρόνο ώστε να μεταβάλλουμε κάποια παράμετρο. Αν ρίξεις μια ματιά στο αρχείο βοήθειας θα δεις ότι δέχεται έξη ορίσματα *kr (start: 0, end: 1, dur: 1, mul: 1, add: 0, doneAction: 0)
. Προς το παρόν μας ενδιαφέρουν τα τρία πρώτα.
Για να δημιουργήσουμε μία νότα με αρχικό πλάτος 0.7 που σβήνει γραμμικά στο 0 μέσα σε 1.5 δευτερόλεπτο χρησιμοποιούμε το Line
μεταβάλλοντας το πλάτος της κυματομορφής όπως στον επόμενο κώδικα.
(
{
var signal, env; // declare variables
// make a straight line from "start-value" to "end-value" in "dur" seconds
env = Line.kr(start: 0.7, end: 0, dur: 1.5);
signal = SinOsc.ar(freq: 220, mul: env); // the enveloped signal
Out.ar([0, 1], signal); // send to both left and right speakers
}.play;
)
Παρατήρησε ότι σε αυτήν την περίπτωση στέλνεται το μήνυμα kr
που σημαίνει ότι τα δεδομένα που φτιάχνει το Line
είναι σε ρυθμό ελέγχου (control rate). Αν δε θυμάσαι τι σημαίνει αυτό γύρισε μερικές σελίδες πίσω και θα το βρεις. Εκείνο που πρέπει να γίνει αντιληπτό είναι ότι το Line
χρησιμοποιείται όχι για να δημιουργήσει ήχο, καθώς αυτή είναι η δουλειά του SinOsc
εδώ, αλλά για να μεταβάλλει την παράμετρο mul του ταλαντωτή. Το συμβολικό γράφημα αυτής της διαδικασίας μοιάζει με το επόμενο.
Όμως στην πράξη, τις περισσότερες φορές μία περιβάλλουσα πλάτους (amplitude envelope) δεν είναι απλώς μια ευθεία αλλά χωρίζεται σε μέρη και περιγράφεται από τις λέξεις: Attack, Decay, Sustain, Release (ADSR). Ας τα δούμε:
Το ακρώνυμο ADSR είναι ένας καθιερωμένος όρος που θα συναντήσεις συχνά σε εγχειρίδια οδηγιών συνθεσάιζερ. Στο SuperCollider για να χρησιμοποιήσεις ένα ADSR envelope ως σήμα ελέγχου για μία παράμετρο πρέπει αρχικά να ορίσεις το σχήμα του και να το αποθηκεύσεις σε ένα αντικείμενο που ονομάζεται Env
. Στη συνέχεια μια γεννήτρια περιβάλλουσας, η EnvGen
(envelope generator), διαβάζει τις αποθηκευμένες τιμές και τις παίζει στον χρόνο.
Ας προχωρήσουμε φτιάχνοντας αρχικά το σχήμα μιας περιβάλλουσας χρησιμοποιώντας το Env
. Ενδιαφερόμαστε για τα τρία πρώτα ορίσματα (levels, times, curve).
Με την επόμενη εντολή αναθέτουμε στη μεταβλητή a ένα envelope. Ορίζουμε τον πίνακα τιμών για τις στάθμες [0, 1, 0.8, 0.75, 0], τον πίνακα τιμών για τους χρόνους μετάβασης [0.05, 0.15, 0.5, 0.2] και ότι τα διαδοχικά τμήματα θα συνδεθούν με ευθείες γραμμές χρησιμοποιώντας το σύμβολο \lin.
a = Env(levels: [0, 1, 0.8, 0.75, 0], times: [0.05, 0.15, 0.5, 0.2], curve: \lin);
Στη συνέχεια στέλνουμε το μήνυμα plot
ώστε να εμφανιστεί το σχήμα σε ένα παράθυρο.
a.plot;
Δες ακόμη μερικά παραδείγματα χρησιμοποιώντας τους ίδιους πίνακες στάθμης και χρόνου αλλά με διαφορετικές συναρτήσεις καμπύλης για τη σύνδεση των τμημάτων.
Env(levels: [0, 1, 0.8, 0.75, 0], times: [0.05, 0.15, 0.5, 0.2], curve: \step).plot;
Env(levels: [0, 0.9, 0.8, 0.75, 0], times: [0.2, 0.15, 0.5, 0.6], curve: \squared).plot;
Env(levels: [0, 1, 0.8, 0.75, 0], times: [0.05, 0.15, 0.5, 0.2], curve: \sine).plot;
Μέχρι εδώ όλα καλά, "jusqu' ici tout va bien" που λένε και οι Γάλλοι. Όπως αναφέρθηκε παραπάνω, συνήθως χρησιμοποιούμε το Env
μαζί με το EnvGen
, η δουλειά του οποίου είναι να παίξει τις αποθηκευμένες τιμές. Αν δεις το κείμενο βοήθειας του EnvGen
θα παρατηρήσεις ότι το πρώτο του όρισμα ονομάζεται envelope, εκεί ακριβώς τοποθετούμε το Env
.
Το επόμενο παράδειγμα μοιάζει αρκετά με τον κώδικα που είδαμε προηγουμένως κάνοντας χρήση του Line
, μόνο που τώρα στη μεταβλητή env έχουμε αναθέσει το αντικείμενο Env
με τα ορίσματά του που φτιάχνει το σχήμα της συνάρτησης. Στη συνέχεια, το env φωλιάζεται εντός του EnvGen
για να παραχθούν οι τιμές στο χρόνο. Είναι πιο εύκολο να το δεις παρά να στο περιγράψω με λέξεις.
(
{
var signal, env; // declare variables
// specify a segmented envelope
env = Env(levels: [0, 1, 0.8, 0.75, 0], times: [0.05, 0.15, 0.5, 0.2], curve: \lin);
signal = SinOsc.ar(freq: 220, mul: EnvGen.kr(envelope: env)); // the enveloped signal
Out.ar([0, 1], signal * 0.5); // send to both left and right speakers
}.play;
)
Το γράφημα παρακάτω παρουσιάζει συμβολικά αυτήν τη διαδικασία που είναι στην πραγματικότητα αρκετά απλή.
Ο κώδικας που ακολουθεί είναι ισοδύναμος με τον προηγούμενο, διαφέρει μόνο στη σύνταξη. Παρατήρησε ότι αντί να χρησιμοποιήσουμε στη θέση του ορίσματος mul το EnvGen.kr(envelope: env)
, μπορούμε απλώς να πολλαπλασιάσουμε το παραγόμενο σήμα του SinOsc
με τη γεννήτρια EnvGen
. Το αποτέλεσμα είναι το ίδιο.
(
{
var signal, env; // declare variables
env = Env(levels: [0, 1, 0.8, 0.75, 0], times: [0.05, 0.15, 0.5, 0.2], curve: \lin);
signal = SinOsc.ar(freq: 220) * EnvGen.kr(envelope: env);
Out.ar([0, 1], signal * 0.5);
}.play;
)
Γενικά μπορείς να σκεφτείς ότι υπάρχουν δύο είδη envelope. Αυτά που έχουν σταθερή διάρκεια και αυτά των οποίων τη διάρκεια μπορούμε να μεταβάλλουμε. Κάτι τέτοιο συμβαίνει σε αντιστοιχία με τη φύση. Π.χ. ένας κρουστός ήχος, όπως είναι ο ήχος του ταμπούρλου, έχει σταθερή διάρκεια. Χτυπώντας τη μεμβράνη του οργάνου δεν μπορούμε να ελέγξουμε πόσο γρήγορα εξασθενεί ο ήχος. Σε αντίθεση, ένα έγχορδο όπως το τσέλο, που παίζεται με δοξάρι, μας επιτρέπει να μεταβάλλουμε τη διάρκεια του σταθερού μέρους του ήχου. Με άλλα λόγια μπορούμε αλλάζοντας δοξαριές να έχουμε έλεγχο πάνω στο μέρος του envelope που διατηρεί την ενέργεια του ήχου (sustain).
Στο SuperCollider το αντικείμενο Env
δημιουργεί οποιοδήποτε σχήμα επιθυμούμε όσο πολύπλοκη μορφή κι αν έχει. Υπάρχουν όμως και μερικές εύχρηστες μέθοδοι που επιτρέπουν να δημιουργούμε εύκολα συνηθισμένα σχήματα σταθερής ή μεταβλητής διάρκειας. Είναι μπόλικες και δε θα ήθελα να σε ζαλίσω, παραθέτω μόνο δύο παραδείγματα για κάθε περίπτωση.
Μία συνηθισμένη μέθοδος που καλείται είναι η linen
που δημιουργεί ένα τραπεζοειδές σχήμα σταθερής διάρκειας. Τα βασικά της ορίσματα αναγνωρίζονται εύκολα από το όνομά τους. Δες τη γραμμή που ακολουθεί.
Env.linen(attackTime: 0.05, sustainTime: 0.5, releaseTime: 1, curve: \lin).plot;
Μία άλλη αρκετά συνηθισμένη μέθοδος είναι η perc
που χρησιμοποιεί ορίσματα για το attack και το release και έχει συνήθως σχήμα που ταιριάζει σε κρουστό ήχο.
Env.perc (attackTime: 0.05, releaseTime: 1, curve: -4).plot;
Οι μέθοδοι σταθερής διάρκειας φαίνονται απλές στη σύνταξη. Φτιάχνουμε κατά τα γνωστά το σχήμα του envelope με το Env
και το φωλιάζουμε ως πρώτο όρισμα της γεννήτριας EnvGen
.
(
{
var signal, env;
env = Env.perc (attackTime: 0.01, releaseTime: 0.5, curve: -4);
signal = FSinOsc.ar(freq: 200, mul: 0.4) * EnvGen.kr(env);
Out.ar([0, 1], signal);
}.play;
)
Στις προηγούμενες γραμμές υπάρχει ένα αντικείμενο που δεν έχουμε συζητήσει, το FSinOsc
, ούτε πρόκειται όμως! θα το καταλάβεις εύκολα ρίχνοντας μια γρήγορη ματιά στο αρχείο βοήθειας. Είναι καλό σταδιακά να αρχίσεις να γνωρίζεις νέα αντικείμενα που δημιουργούν ήχο γιατί αλλιώς θα βαρεθείς. Το αφήνω πάνω σου.
Όταν χρησιμοποιούμε περιβάλλουσες μεταβλητής διάρκειας (στα αγγλικά ονομάζονται sustained envelopes) τα πράγματα γίνονται λίγο πιο περίπλοκα. Ας δούμε δύο παραδείγματα τέτοιων σχημάτων.
Η μέθοδος adsr
π.χ. δημιουργεί ένα ADSR envelope, ενώ η μέθοδος asr
χρησιμοποιεί μόνο τρία ορίσματα (attack, sustain και release - ASR) και πρόκειται για μια πιο απλή μορφή του ADSR παραλείποντας το μέρος της εξασθένησης του σήματος.
Env.adsr(attackTime: 0.05, decayTime: 0.2, sustainLevel: 0.5, releaseTime: 1, curve: -2).plot;
Env.asr(attackTime: 0.05, sustainLevel: 0.5, releaseTime: 1, curve: -2).plot;
Το όρισμα sustainLevel και στις δύο παραπάνω περιπτώσεις καθορίζει τη στάθμη του σταθερού μέρους του envelope ως ποσοστό της μέγιστης τιμής του.
Στις περιβάλλουσες μεταβλητής διάρκειας, όπως το Env.adsr
και το Env.asr
, μας είναι απαραίτητο το δεύτερο όρισμα της γεννήτριας EnvGen
που ονομάζεται gate (πύλη). Η δουλειά του είναι να «πυροδοτήσει» το envelope, να δώσει το έναυσμα δηλαδή ώστε να ξεκινήσει η παραγωγή των τιμών. Όταν λάβει τιμή μεγαλύτερη του 0 τότε δίνει σήμα στο EnvGen
να παίξει το envelope το οποίο θα παραμείνει ενεργό έως ότου το gate γίνει 0, οπότε και θα πεθάνει μέσα στον χρόνο που ορίζει το release. Δύσκολο; Τρέξε τις επόμενες γραμμές και κούνα τον κέρσορα του ποντικιού από το ένα άκρο της οθόνης στο άλλο.
(
{
var env, noise, sig ;
env = EnvGen.ar(envelope: Env.asr(0.01, 0.5, 0.6), gate: MouseX.kr(-1, 1));
noise = LFTri.ar(freq: 300, mul: 0.5);
sig = noise * env;
Out.ar([0, 1], sig);
}.play;
)
Το MouseX.kr(-1, 1)
, που βρίσκεται στη θέση του gate, δίνει τιμές μεταξύ (-1, 1), ανάλογα με την οριζόντια θέση του κέρσορα. Όταν αυτός βρίσκεται στο δεξί μέρος της οθόνης το gate πυροδοτεί το envelope, το οποίο παραμένει ανοιχτό μέχρι να μετακινήσουμε τον κέρσορα πέραν του μέσου της οθόνης ώστε να παραγάγουμε τιμή μικρότερη του 0. Παρατήρησε ότι ο ήχος δε σταματά ακαριαία αλλά σβήνει σταδιακά μέσα σε 0.6 δευτερόλεπτα, όσος και ο χρόνος που έχει τεθεί στο release.
Αν το σχήμα είναι σταθερό, όπως στην περίπτωση των μεθόδων linen
και perc
, τότε το gate απλώς θέτει σε λειτουργία το envelope, δημιουργώντας ένα ηχητικό γεγονός συγκεκριμένης διάρκειας.
(
{
var env, noise, sig ;
env = EnvGen.ar(envelope: Env.perc(0.01, 0.5), gate: MouseX.kr(-1,1));
noise = LFTri.ar(freq: 440, mul: 0.5);
sig = noise * env;
Out.ar([0, 1], sig);
}.play;
)
Το όρισμα gate λοιπόν του EnvGen
θέτει σε κίνηση μία περιβάλλουσα ανάλογα με την τιμή που λαμβάνει. Γενικά η ενέργεια της πυροδότησης μιας διαδικασίας στην αγγλική ορολογία ονομάζεται triggering. Έτσι στην ελληνική αργκό συχνά λέμε ότι το gate «τριγκάρει» το envelope, είναι αυτό δηλαδή που το θέτει σε λειτουργία.
ΟΚ. Πριν προχωρήσουμε στο επόμενο κεφάλαιο και αρχίσουμε να εξετάζουμε μια πολύ βασική μορφή σύνθεσης ήχου, την προσθετική, θα ήθελα να επιστήσω την προσοχή σου σε κάτι που πιθανά έχεις σκεφτεί. Αν παρατηρήσεις, όλες οι προηγούμενες ηχητικές δομές χρησιμοποιούν μία περιβάλλουσα πλάτους (amplitude envelope), αλλάζοντας έτσι την ένταση του ήχου στη διάρκεια του χρόνου. Επειδή όμως μία περιβάλλουσα δεν είναι τίποτα παραπάνω από ένα σχήμα, μπορεί κατ' αντιστοιχία να χρησιμοποιηθεί για να επηρεάσουμε οποιαδήποτε ηχητική παράμετρο. Μία συνήθης περίπτωση είναι να μεταβάλουμε τη συχνότητα ενός ταλαντωτή, κατασκευάζοντας με αυτόν τον τρόπο μία περιβάλλουσα συχνότητας (frequency envelope).
Παρατήρησε τις επόμενες γραμμές. Η θεμελιώδης συχνότητα του ταλαντωτή μεταβάλλεται με τη χρήση περιβάλλουσας από τα 200 στα 500 Hz μέσα σε 2 δευτερόλεπτα και από τα 500 στα 300 Hz μέσα σε 1.5 δευτερόλεπτο. Αν τρέξεις τον κώδικα θα ακούσεις ότι ο ήχος δε σταματά αφού τώρα δεν επηρεάζεται καθόλου το πλάτος. Μπορείς όμως αν θέλεις να δοκιμάσεις να προσθέσεις μία περιβάλλουσα πλάτους σύμφωνα με όσα ήδη ξέρεις.
(
{
var signal, freqEnv;
freqEnv = Env(levels: [200, 500, 300], times: [2, 1.5]);
signal = Pulse.ar(freq: EnvGen.kr(freqEnv), mul: 0.1); // a frequency envelope
Out.ar([0, 1], signal);
}.play;
)
Υπάρχουν πράγματα ακόμη να πούμε σχετικά με αυτό το θέμα. Θα επανέλθουμε όμως αργότερα όταν θα έχεις χτίσει περισσότερη γνώση γύρω από τον τρόπο λειτουργίας του SuperCollider.