Sei sulla pagina 1di 18

Sintetizzatore di suoni

sintetizzatore audio Web


Sergey Alexandrovich Kryukov
23 febbraio 2023
MIT

Sintetizzatore nel browser: creare strumenti da utilizzare in applicazioni musicali, con


tecniche avanzate di sintesi additiva e sottrattiva

Questo sintetizzatore da browser crea strumenti da utilizzare in applicazioni musicali, e


offre tecniche avanzate di sintesi additiva e sottrattiva. Si basa su Web Audio API, e si
basa solo sul browser (non richiede alcuna parte lato server). Crea sintetizzatori per
strumenti musicali, che possono essere esportati e incorporati in un'applicazione Web.
Contenuti
o Introduction
o Motivation
o Advanced features
o A Word of Warning and a Disclaimer
o Audio Graph
o Main Oscillator
▪ Wave FFT
o Oscillator Control
o Modulation
o Envelopes
▪ Volume Envelope
▪ Detune Envelope
▪ Modulation Envelopes
o Filters
o Gain Compensation
o Using API and Generated Instruments In Applications
▪ API
o Interesting Implementation Detail
▪ Classes with Private Members
▪ Initialization
o Compatibility
o Live Play
o Credits
o Conclusions

Introduzione
Questo è il terzo articolo della serie dedicata allo studio musicale con le tastiere a
schermo, comprese quelle microtonali:
• Studio musicale con la tastiera del computer isomorfo
• Microtonal Music Study with Chromatic Lattice Keyboard (attualmente l'articolo
è in fase di ricostruzione relativo al passaggio al Microtonal Fabric)
• Il presente articolo
Attualmente, tutto il software basato su Web Audio API è integrato in un unico
progetto Microtonal Fabric .

Motivazione
La principale forza motrice del presente lavoro è il mio forte bisogno di uno strumento
simile. È correlato al repository del mio lavoro Microtonal Music Study utilizzando
tastiere cromatiche specializzate e API Web Audio.
Due articoli relativi a questo argomento sono stati pubblicati come Musical Study with
Isomorphic Computer Keyboard e Microtonal Music Study with Chromatic Lattice
Keyboard (anche se il primo articolo non ha nulla a che fare con i sistemi microtonali,
fornisce però alcune spiegazioni teoriche di base sull'argomento).
La tastiera di cui al secondo articolo è microcromatica, molto innovativa, e funziona in
un browser Web con l'utilizzo delle API di WEB Audio. Di recente, WEB Audio ha
utilizzato il mio fork, pesantemente modificato, in una libreria open source di terze
parti, con difetti molto gravi di cui non voglio nemmeno discutere qui. Ad ogni modo, il
mio fork ha già svolto il suo ruolo quando era più importante la dimostrazione della
mia idea. In questi giorni, continuo la ricerca e lo sviluppo e non posso tollerare quei
difetti e la manutenibilità insufficiente.
Ma è molto difficile (o addirittura impossibile) trovare un sintetizzatore open source
decente, che soddisfi tutte le mie esigenze; quindi, ho deciso di svilupparne uno mio. In
altre parole, ho iniziato il lavoro solo perché lo desideravo a tutti i costi per ottenere il
risultato. Di recente, ho presentato i miei risultati ad alcuni musicisti di spicco,
specialisti in musica microtonale, teoria e pedagogia, e ho ricevuto feedback molto
positivi. Penso che lo strumento sia avanzato, accurato e abbastanza interessante da
condividerlo.

Funzionalità avanzate
Lo strumento è un sintetizzatore sintetizzatore o un generatore di istanze dello
strumento che possono essere esportate sotto forma di un singolo file JSON,
incorporato in qualche altro codice JavaScript e utilizzato per l'implementazione di uno
strumento musicale nel browser basato su Web Audio API , o qualche altro strumento
per la generazione di musica su una pagina Web. Lo strumento è orientato sia ad
applicazioni musicali di uso comune che microtonali o xenarmoniche .
Nessuna parte del codice utilizza alcuna operazione del sito del server, quindi qualsiasi
parte del codice può essere riprodotta localmente su un'ampia gamma di dispositivi,
anche senza connessione a Internet.
Alcune funzionalità dello strumento sono in qualche modo innovative. Nel complesso,
aiuta a generare suoni strumentali quasi realistici, definendo effetti avanzati in modo
grafico utilizzando una comoda interfaccia utente. Non è necessario che l'utente sia in
grado di lavorare con i nodi audio, di disegnare qualsiasi grafica o di comprendere l'API
Web Audio. Invece, la procedura di progettazione di uno strumento si basa sul
riempimento di dati in diverse tabelle, possibilmente un approccio per tentativi ed
errori con l'ascolto dei risultati intermedi della sintesi.
La procedura di creazione dello strumento parte da un singolo oscillatore definito da
uno spettro di Fourier. Tale spettro può essere importato da un analizzatore spettrale di
un campione di codice disponibile, ma di solito è necessaria un'edizione aggiuntiva. In
alternativa, sono disponibili anche le tradizionali forme d'onda del segnale, come il
dente di sega o il triangolo, ma suoni più realistici richiedono l'approccio basato su
Fourier.
Inoltre, l'utente può definire un numero illimitato di modulatori. Un modulatore può
essere utilizzato per la modulazione di frequenza o di ampiezza e ciascuno di essi può
essere un modulatore di frequenza assoluta o di frequenza relativa. Per le modulazioni
assolute, l'utente può definire una frequenza fissa per ognuna, ma per le modulazioni a
frequenza relativa, la frequenza del segnale di modulazione dipende dalla frequenza
fondamentale di ogni tono.
Inoltre, il risultato della sintesi può essere modellato utilizzando una busta. A differenza
dei sistemi di sintesi convenzionali, oltre al normale inviluppo di volume, ci sono tre tipi
aggiuntivi di inviluppo: uno per la desintonizzazione temporanea e due per le
modulazioni, separatamente per la modulazione di frequenza e di ampiezza.
Oltre a questo, c'è una serie di filtri con parametri definiti dall'utente. Qualsiasi
sottoinsieme di filtri può essere incluso in uso o escluso.
Infine, oltre a questo, c'è un sistema di compensazione del guadagno.
Durante il lavoro, l'autore di uno strumento utilizza una serie di strumenti interattivi
utilizzati per ascoltare i risultati intermedi. C'è una tastiera di prova in stile Jankó,
corrispondente al pianoforte standard a 88 tasti senza un tono più alto. La tastiera può
essere giocata con un mouse o con tutte e 10 le dita utilizzando un touch screen. Per
entrambi i metodi di gioco è possibile anche il glissando, che è un aspetto tecnico in
qualche modo non banale. Un'altra caratteristica importante è questa: durante il gioco
di prova, qualsiasi classe degli effetti può essere temporaneamente attivata, il che è
importante per i confronti. Inoltre, la riproduzione di prova può essere eseguita con il
controllo di volume, sustain (aggiuntivo) e trasposizione. Questi controlli non fanno
parte degli strumenti risultanti e sono progettati specificamente per l'esecuzione di
prova.

Dichiarazione di non responsabilità


La sintesi è una cosa strana. Se usi questo strumento, assicurati di procedere con
attenzione, per proteggere il tuo udito musicale da shock potenzialmente
considerevoli. In alcuni casi, errori accidentali nei parametri possono produrre un suono
orribile, quasi traumatizzante (non vedo alcuna possibilità di filtrare automaticamente
le cose cattive… inoltre, sarebbe ingiusto).
Non posso assumermi alcuna responsabilità per la produzione di suoni cattivi o
sgradevoli, ma posso assicurare che la produzione di suoni ragionevolmente piacevoli è
del tutto possibile. Ad ogni modo, fornirò anche una libreria di alcuni campioni
gradevoli di strumenti.

Grafico audio
Prima di tutto, la caratteristica di progettazione più importante è questa: tutti i tasti
della tastiera su schermo dovrebbero essere in grado di generare suoni
contemporaneamente. Ci si potrebbe chiedere: perché dovremmo aver bisogno di tutti
loro — se suoniamo con solo 10 dita? Semplice: questo è dovuto al sustain di uno
strumento. Ad esempio, se premiamo un pedale sustain di un pianoforte e lo teniamo
premuto, in linea di principio potremmo colpire rapidamente tutte le 88 corde… quindi,
in un certo momento tutte potrebber generare un suono (non importa quanto folle
questo possa sembrare).
Per raggiungere questo obiettivo, dobbiamo dedicare un numero considerevole di nodi
a ogni singolo tasto/chiave dello strumento. Queste parti del grafico sono
implementate da un oggetto di tipo Tone. Tuttavia, è bene ridurre anche al minimo il
numero di questi nodi per ogni tasto. Ciò può essere fatto condividendo alcuni dei
nostri effetti audio in un altro oggetto, di tipo Instrument.
Per prima cosa, vediamo cosa possiamo condividere.
Il set di filtri può essere condiviso.
Alcune modulazioni possono essere condivise, ma alcune dovrebbero essere riservate a
ciascun tasto.
Ho sviluppato quattro tipi di modulazione. Possiamo applicare un numero illimitato di
modulatori di frequenza (FM) e modulatori di ampiezza (AM). Ciascuno di questi
modulatori può essere a frequenza assoluta, o a frequenza relativa. Tutti i modulatori di
frequenza assoluta possono essere inseriti nell'unico Instrument, e le istanze Tone
possono condividerli. Ma i modulatori di frequenza relativa modulano a una frequenza
diversa per ciascuno dei toni. Nello strumento, la frequenza effettiva è la frequenza
fondamentale di ogni tono, semplicemente moltiplicata per qualche fattore, con
profondità di modulazione fisse, individuali per ogni modulatore.
Pertanto, entrambi (Instrument e Tone) sono basati sullo stesso tipo di dato
denominato ModulatorSet. Per l'implementazione di un insieme di modulatori, non
importa quale ruolo svolga (frequenza assoluta o frequenza relativa), l'implementazione
è la stessa.
Un'altra parte del grafico per ciascun tono è un insieme di nodi di guadagno, utilizzati
come obiettivi per gli inviluppi . Un inviluppo è un meccanismo utilizzato per
programmare il comportamento dinamico di un tono nel suo attacco e
smorzamento. La consueta tecnologia di sintesi di solito implementa solo l'inviluppo
che controlla il volume, con un numero fisso di stadi. L’inviluppo solito è detto ADSR
(Attack, Decay, Sustain, Release), ma non voglio discuterne qui, perché non mi
soddisfa.
Sound Builder dispone di un sistema unificato di creazione di inviluppi, di diversi tipi,
che rende possibile la creazione di inviluppi con un numero illimitato di stadi. Ogni fase
è caratterizzata dal tempo, dal guadagno e dalla scelta di una delle tre
funzioni. Esistono quattro caratteristiche di un nodo e ciascuna di esse può essere
programmata in busta: volume, detune, AM e FM.

Ora siamo pronti a presentare un grafico, partendo da una parte tonale. In primo
luogo, introduciamo alcune convenzioni grafiche.
Nodo oscillatore

Guadagno nodo

Inviluppo (o nodo di guadagno controllato dalle funzioni di inviluppo)

Kit modulatore

Catena di nodi

Nodo di uscita

Il grafico è mostrato schematicamente in due parti, sinistra e destra. A sinistra, c'è


un'istanza singola di Instrument connesso con un insieme di istanze Tone (mostrate
come una sola a sinistra). Ricordo che esiste un intero insieme di istanze Tone, tutte con
frequenze fondamentali diverse. Ogni istanza Tone è collegata all'istanza dello
strumento in tre modi. Un'istanza Instrument fornisce due segnali dai set di
modulatori FM e AM, che sono comuni a tutti i toni, e riceve un segnale sonoro
completo da ciascun tono, modulato e controllato dalle funzioni degli inviluppi.
Ci sono quattro tipi di oscillatori. Il primo fornisce un segnale di una frequenza
fondamentale per ogni tono. Due insiemi di oscillatori in ogni grafico dei toni
forniscono segnali FM e AM di frequenze dipendenti dalla frequenza fondamentale, e
altri due insiemi di oscillatori forniscono segnali FM e AM di frequenze fisse a tutti i
grafici dei toni.
All'inizio dell'operazione, non appena l'intero grafico è popolato e connesso, tutti gli
oscillatori si avviano e forniscono i loro segnali in modo permanente, anche quando
non viene emesso alcun suono. I segnali sonori dai grafici dei toni sono bloccati dai
loro inviluppi di guadagno. Gli oscillatori si fermano solo quando il grafico deve essere
ripopolato in base ai nuovi dati dello strumento.
Consideriamo dapprima il tipo principale di oscillatore, quello che fornisce la base del
suono. Naturalmente, in assolutamente tutti i casi, fornisce un intero spettro di
frequenze basato sulla sua frequenza fondamentale.

Oscillatore principale
Questo nodo viene creato dal costruttore Web Audio OscillatorNode.
L'utente seleziona lo spettro del nodo assegnando un valore alla proprietà type, che
può essere un valore di stringa, "dente di sega", "triangolo" o personalizzato. Nel caso
di "custom", questa stringa non deve essere assegnata. Invece, viene chiamato il
metodo setPeriodicWave. Nell'interfaccia utente di Sound Builder, corrisponde alla
scelta "Fourier".
Questa chiamata è un “cavallo da lavoro” di sintesi. I dati per l'onda sono forniti
dall'istanza di Instrument e sono condivisi da tutti i toni. Questo perché le
componenti dello spettro di Fourier sono relative alla frequenza fondamentale.
Nell'interfaccia utente di Sound Builder, lo spettro non è rappresentato da un array o
da un numero complesso, ma da un array equivalente di ampiezze e fasi, che l'utente
può "disegnare" con il mouse su una tabella di cursori. Ogni dispositivo di scorrimento
è un elemento di input con type="range" con un comportamento modificato che
consente l'azione del mouse simile al disegno.

FFT dell’Onda
Un modo alternativo per inserire i dati consiste nell'utilizzare un'applicazione separata
"WaveFFT.exe" (da file WAV alla Fast Fourier Transform). Può caricare un file WAV,
osservarne la forma d'onda, selezionare un frammento della sequenza di campioni (la
FFT supporta un numero di campioni pari a potenze del 2, e questo è supportato
dall'interfaccia utente), eseguire la FFT, osservare lo spettro risultante e salvare questi
dati nel formato dei files di dati dello strumento Sound Builder.
In genere, il file di dati creato con WaveFFT funge da punto di partenza. Un altro modo
per iniziare è utilizzare alcuni file di esempio scaricabili con questo articolo.
Questa applicazione potrebbe essere oggetto di un articolo a parte. In breve, questa è
un'applicazione WPF con Core .NET, quindi può essere eseguita su piattaforme diverse
senza ricostruzione. Attualmente, include versioni per Windows, Linux e Mac OS. È
necessario installare il framework appropriato.
Controllo dell'oscillatore
Con le API di Web Audio, un nodo può essere connesso a un altro nodo oa un oggetto
che è una proprietà di un nodo, se questo oggetto supporta
l'interfaccia AudioParam. Questo è un modo per modulare il valore corrispondente
all'uno o all'altro parametro audio.
OscillatorNode ha due proprietà a-rate , che supportano l'interfaccia AudioParam:
frequency e detune.
L'ingresso Frequency AudioParam può essere utilizzato per la FM, che può essere
inviluppato facoltativamente. Lo stesso può essere fatto per detune, ma la modulazione
del parametro detune avrebbe poco senso. Invece, può essere modificato tramite un
inviluppo.

Modulazione
Due tipi di modulazione sono implementati in modi diversi: il segnale FM si collega
all'ingresso di frequenza di qualche oscillatore, mentre l'AF deve essere fatto sull’uscita,
per modulare il guadagno di un segnale, che è già stato generato e modulato in
frequenza.
Pertanto, il segnale FM è collegato a una frequenza di nodo dell'oscillatore AudioParam
come spiegato prima, e il segnale AM è collegato a qualche nodo di guadagno.
In entrambi i casi, i segnali FM e AM sono collegati a qualche nodo di guadagno,
mostrati sul grafico rispettivamente come "FM Envelope" e "AM Envelope".
Ognuna di queste modalità di guadagno riceve segnali FM e AM da due sorgenti,
segnali di modulazione di frequenza assoluta dall'istanza Instrument e segnali di
modulazione di frequenza relativa dall'interno della stessa istanza Tone.
In più, ricordiamo che anche i nodi di guadagno “FM Envelope” e “AM Envelope” sono
inviluppi. Il che significa che il valore di guadagno di questi nodi viene mantenuto a
zero finché un esecutore non preme un tasto. Quando ciò accade, una funzione di
inviluppo viene applicata al guadagno, per aprire ai segnali l’uscita. Vediamo come
funziona.

Inviluppi
Il funzionamento degli inviluppi si basa sulla funzionalità di AudioParam . Ogni istanza
di AudioParam può essere programmata per modificare automaticamente il valore in
un futuro momento. Quando un esecutore preme un tasto sulla tastiera di uno
strumento, il guadagno e alcuni altri parametri audio sono programmati per
implementare alcune dinamiche.
Envelope definisce una funzione del tempo, controllando il valore di
un'istanza AudioParam. Questa è una funzione graduale a tratti, composta dalla 3 tipi di
funzionalità: la funzione esponenziale, la funzione lineare e, con un certo grado di
convenzionalità, la meno utilizzabile funzione di Heaviside. Per ogni tratto, l'utente
aggiunge una riga a una tabella di inviluppo, e definisce la durata di ogni step e un
valore di target.
Questo è il core dell'implementazione di envelope da "sound/envelope.js":
javascript
for (let element of this.#implementation.data) {
switch (element.type) {
case this.#implementation.typeHeaviside:
audioParameter.setValueAtTime(element.gain, currentTime +
element.time);
break;
case this.#implementation.typeExponential:
if (element.gain == 0 || element.isLast)
audioParameter.setTargetAtTime(element.gain, currentTime,
element.time);
else
audioParameter.exponentialRampToValueAtTime(
element.gain,
currentTime + element.time);
break;
case this.#implementation.typeLinear:
audioParameter.linearRampToValueAtTime(element.gain, currentTime +
element.time);
break;
} //switch
currentTime += element.time;
}

È importante notare che la funzione esponenziale viene implementata in due modi


diversi: tramite setTargetAtTime o exponentialRampToValueAtTime Il primo caso viene
utilizzato quando un valore target è zero, lo stadio è l'ultimissimo. Si fa così perché, per
ovvi motivi matematici, il valore di target non può essere zero per l'esponente. Quindi
la funzione rappresenta l'approccio "infinito" (asintotico) di un valore AudioParam al
valore di target. Naturalmente, per l'ultimo stadio, tale comportamento rappresenta lo
smorzamento naturale o di un suono di strumento, o l'accrescimento del parametro
fino ai valori che corrispondono al suono stabilizzato, nullo o non nullo. In entrambi i
casi, il comportamento esponenziale "infinito" è il più adatto.
Con Sound Builder, gli inviluppi possono essere opzionalmente applicati a tre
caratteristiche di un suono: il volume, il detune, la FM e la AM. Parliamo
dell'applicazione di queste tecniche.
Inviluppo volumetrico
Questo è il tipo di busta più tradizionale, che viene utilizzato, ad esempio, negli
ADSR. La differenza non è solo il numero illimitato di stadi. Un'ulteriore caratteristica
dell'inviluppo del volume è il suo sustain “dumping”, che si scarica. Non esiste un
suono che sia interrotto all'istante. Se proviamo a produrre un tale arresto istantaneo, o
comunque non istantaneamente ma molto rapidamente, l'elettronica produce uno
spettro naturalmente così ampio da venire percepito come un rumore di crepitio, che è
molto sgradevole. La stessa cosa può accadere se un attacco è troppo veloce. Non
credo che possa essere praticamente utilizzato alcun timing più veloce di circa 10 ms,
perciò i tempi dello stadio di inviluppo sono limitati da questa durata. Il dumping del
sustain si verifica quando, ad esempio, suoniamo un pianoforte e rilasciamo un
tasto. Ma cosa succede quando teniamo premuto il tasto o usiamo un pedale di
sustain? In questo caso, il comportamento è definito dall'ultima fase di un inviluppo.
Possiamo definire essenzialmente due diversi tipi di strumenti. Il primo tipo è adatto
allo strumento con smorzamento naturale, come il pianoforte o le campane. Per questi
strumenti, il valore target del guadagno dell'ultimo stadio dovrebbe essere zero, ma lo
stadio stesso può essere prolungato, diciamo, fino a diversi secondi. Se rendiamo
questo tempo di circa 10 millisecondi o forse alcune volte maggiore, possiamo
ottenere l'effetto più vicino a qualche percussione melodica. Il secondo tipo può
modellare lo strumento dal suono "infinito", come negli strumenti a fiato o ad arco. Il
guadagno target dell'ultimo stadio dell'inviluppo dovrebbe essere di valore massimo, o
quasi. In questo caso, il suono viene smorzato solo quando un esecutore rilascia un
tasto.
Inviluppo di Detune
Non è insolito che uno strumento suoni un tono che, per un certo periodo di tempo,
viene stonato, specialmente durante la fase di attacco. Ad esempio, accade quando si
suona la chitarra fingerstyle ad alto volume. Nella prima fase di pizzicamento la corda è
notevolmente allungata e quindi suona più acuta del tono su cui è accordata. Inoltre,
all'inizio, una corda può essere premuta più forte contro il tasto. Questo effetto può
essere ottenuto controllando un certo valore di detune dell'oscillatore. Naturalmente, a
differenza del caso del volume, il valore di detune dell'ultimo stadio dovrebbe essere
zero.
L'inviluppo Detune è l'unico inviluppo non rappresentato nel grafico audio come
modalità di guadagno controllata dall'inviluppo. Il suo target è il detune AudioParam di
ogni oscillatore principale di un tono. Nessun nodo di guadagno è richiesto per questa
funzionalità. Il guadagno master dell'effetto è semplicemente un fattore numerico; tutti
i valori target, tutti gli stadi dell'inviluppo vengono moltiplicati per questo fattore.
Inviluppi di modulazione
I due inviluppi rimanenti sono quelli che separatamente controllano AM e FM, ma non
si fa alcuna distinzione tra le FM/AM a frequenza assoluta, e le FM/AM a frequenza
relativa. Invece, un inviluppo controlla tutti gli AM, e l’altro tutti i FM. Consideriamo un
caso tipico di tale inviluppo. Una tuba, o un sassofono, così come molti altri strumenti a
fiato, mostrano vibrazioni estremamente forti quando un esecutore inizia a soffiare un
suono, ma la vibrazione diventa appena percettibile quando il suono si stabilizza. Ciò
può essere ottenuto con questi due tipi di inviluppo.

Filtri
La parte sottrattiva della sintesi è rappresentata da un insieme di filtri biquadratici di
basso ordine. Un certo filtraggio si rende necessario nella maggior parte dei casi.
I parametri del filtro vengono inseriti in una tabella con l'aiuto di cursori. Ognuno dei
tipi di filtro forniti può essere incluso nel grafico dello strumento oppure può essere
escluso. Nella maggior parte dei casi è sufficiente un solo filtro passa-basso.
Per apprendere l'ottimizzazione dei filtri, un buon punto di partenza potrebbe essere la
documentazione di BiquadFilterNode.

La compensazione del guadagno


Fourier, e la sintesi sottrattiva basata sui filtri, rendono difficile prevedere il volume
finale di ogni tono. La cosa peggiore è che tutti i toni producono livelli particolari di
volume molto diversi, a seconda della frequenza, e anche il volume complessivo di uno
strumento può differire notevolmente. Perciò, è assolutamente necessaria una certa
compensazione del guadagno che dipenda dalla frequenza. Ho fatto un gran numero
di ricerche utilizzando varie funzioni "intelligenti", ma alla fine sono arrivato
all'approccio più semplice, una spline quadratica definita da tre punti:
- una frequenza bassa (e un livello di compensazione per tale frequenza);
- un'altra frequenza, alta (e un livello di compensazione per tale frequenza);
- qualche punto di frequenza intermedia frequenza in cui il guagagno viene
considerato pari a 1.
- Naturalmente, a questo punto vengono cucite due funzioni
quadratiche. Questa semplice funzione ha il seguente vantaggio: si può
compensare parte dello spettro a destra di questo punto, poi, separatamente,
sulla parte sinistra dello spettro. Quando la parte sinistra o destra è
terminata, il compenso per un'altra parte non rovinerà il lavoro precedente.
Questa procedura richiede una tabella con tre parametri:
1. compensazione a bassa e alta frequenza;
2. posizione della frequenza media;
3. valore del fattore di compensazione complessivo.
Due valori per le stesse frequenze basse e alte non sono presentati nella tabella, perché
raramente, se non mai, devono essere modificati; corrispondono alle frequenze più alte
o più basse di un pianoforte standard a 88 tasti. Tuttavia, sono prescritti nei file di dati
(dove sono scritti in base al set di definizioni del codice) e possono essere modificati da
alcuni utenti eccezionalmente intelligenti. :-).
Questa è l'implementazione di questa semplice funzione spline:
javascript
const compensation = (() => {
const compensation = (f) => {
const shift = f - this.#implementation.gainCompensation.middleFrequency;
const g0 =
this.#implementation.gainCompensation.lowFrequencyCompensationGain;
const g1 =
this.#implementation.gainCompensation.highFrequencyCompensationGain;
const f0 = this.#implementation.gainCompensation.lowFrequency;
const f1 = this.#implementation.gainCompensation.lowFrequency;
const factor0 = (g0 - 1) / Math.pow(
f0 - this.#implementation.gainCompensation.middleFrequency, 2);
const factor1 = (g1 - 1) /
Math.pow(f1 - this.#implementation.gainCompensation.middleFrequency,
2);
if (shift < 0)
return 1 + factor0 * Math.pow(shift, 2);
else
return 1 + factor1 * Math.pow(shift, 2);
} //compensation
return compensation;
})(); //setupGain

La cucitura avviene nel punto di media frequenza (più precisamente nel punto di
derivata zero), ma con un passaggio in derivata seconda. Questa imperfetta levigatezza
può sembrare discutibile, ma dopo alcuni esperimenti, sembra produrre un suono
levigato (gioco di parole non intenzionale ;-).
La funzione di compensazione dipende dalla frequenza fondamentale di ciascuno degli
oscillatori principali, il valore della compensazione del guadagno è combinato come
risultato dell'operazione di molti nodi di guadagno, i nodi di guadagno di
"Compensazione", uno per istanza Tone, più un nodo dell'istanza Instrument,
“Compensazione del guadagno”, come mostrato nel grafico.

Utilizzo delle API e strumenti generati nelle


applicazioni
Naturalmente, Sound Builder non fa molto se non viene utilizzato in altre
applicazioni. Vedere le istruzioni in https://SAKryukov.github.io/microtonal-chromatic-
lattice-keyboard/SoundBuilder/API.html .
Fondamentalmente, l'utente deve produrre un set di strumenti desiderati e testarli.
1. Produrre alcuni set di strumenti, testarli e salvarli, ogni strumento è un file JSON
separato;
2. Usare l'elemento “Instrument List”, il button “Load” per caricare gli strumenti creati,
“Save” per salvare il tutto in un unico file .js;
3. Includere l'API, tutti i file sound/*.js nell'applicazione;
4. Includere anche il file .js generato in precedenza;
5. Nell'applicazione, creare un’istanza di Instrument;
6. Per applicare i dati dello strumento, assegnare
instrumentInstance.data = instrumentList[someIndex];
7. Per riprodurre un singolo suono, richiamare instrumentInstance.play.
Ora, vediamo in dettaglio come appare l'API.
API
Il costruttore Instrument si aspetta di ricevere 2
argomenti: constructor(frequencySet, tonalSystem).
Prima di tutto, occorre definire un insieme di frequenze fondamentali per i suoni
generati. Si tratta di un array di frequenze in unità Hertz o di un oggetto {first,
last, startingFrequency}, dove first è sempre zero (è riservato a funzionalità
avanzate), last è l'ultimo indice dell'array di frequenze ed startingFrequency è la
frequenza assoluta più bassa in unità Hertz (27,5 Hz per il pianoforte standard a 88
tasti). Il secondo parametro dovrebbe specificare il sistema tonale, pari al numero di
toni in ottava. In questo caso, il tone set verrà popolato automaticamente con le
frequenze di Equal Division of the Octave (EDO).
Per esempio:
const piano = new Instrument({ first: 0, last: 87, 25.7 }, 12)
creerà un'accordatura di pianoforte standard a 88 tasti e i tipici sistemi microtonali
potrebbero utilizzare 19, 29, 31, 41, ecc., per il secondo argomento.
const perfect = new Instrument([100, 133.333, 150])
creerà uno strumento con solo 3 toni, 100 Hz, e altri due toni, quarta giusta e quinta
giusta nella sola intonazione, o intonazione pura.
Il secondo parametro, tonalSystem, viene utilizzato anche quando un array di valori a
frequenza fissa — per l'implementazione della proprietà transposition. Se questo
argomento non è specificato, si assume la lunghezza di questo array.
Il metodo play(on, index) avvia (se on==true) o arresta (se on==false) gli
smorzamenti di un tono specificato index. L'avvio dello smorzamento non è mai
istantaneo, ed è definito dall'inviluppo del guadagno e dal parametro di sustain dello
smorzamento. La temporizzazione è limitata da alcuni valori minimi.
Proprietà: volume (da 0 a 1), sustain (tempo in secondi) e transposition (nelle
unità di divisioni logaritmiche uguali di ottava, a seconda dell'argomento tonalSystem
del costruttore Instrument). La proprietà frequencies è di sola lettura, e restituisce
l'array che rappresenta il set di frequenza corrente.
Per includere l'API, utilizzare questo esempio di codice:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<script src="../sound/definition-set.js"></script>
<script src="../sound/chain-node.js"></script>
<script src="../sound/envelope.js"></script>
<script src="../sound/modulator.js"></script>
<script src="../sound/modulator-set.js"></script>
<script src="../sound/tone.js"></script>
<script src="../sound/instrument.js"></script>
<!-- ... --->
</head>

<body>

<!-- ... --->

</body>
</html>

Dettaglio interessante di implementazione


Non serve molto codice per spiegare i principi di funzionamento, comunque
il grafico spiega al meglio l'essenza di questa tecnologia, ma è bene condividere anche
alcune particolarità tecniche.

Classi con membri privati


Dopo alcuni esperimenti e riflessioni, ho sviluppato una disciplina conveniente per
l'utilizzo delle classi JavaScript, solo per i casi in cui sembra essere conveniente, con
separazione tra membri pubblici e privati . Per ridurre la possibile confusione, ho deciso
di concentrare tutti i membri privati di un singolo oggetto denominato
uniformemente #implementation.
Per dimostrarlo, mostro la più semplice delle classi del suono, Modulator. È una cosa
piuttosto banale, una coppia connessa di istanze di OscillatorNode e GainNode :
javascript
Riduci ▲
class Modulator {

#implementation = { isEnabled: true };

constructor(audioContext) {
this.#implementation.oscillator = new OscillatorNode(audioContext);
this.#implementation.depthNode = new GainNode(audioContext);
this.#implementation.depthNode.gain.value = 100;
this.#implementation.oscillator.connect(this.#implementation.depthNode);
this.#implementation.oscillator.start();
this.#implementation.output = this.#implementation.depthNode;
this.#implementation.deactivate = _ => {
this.#implementation.oscillator.stop();
this.#implementation.oscillator.disconnect();
this.#implementation.depthNode.disconnect();
}; //this.#implementation.deactivate
} //constructor

get frequency() { return this.#implementation.oscillator.frequency.value; }


set frequency(value) { this.#implementation.oscillator.frequency.value =
value; }
get depth() { return this.#implementation.depthNode.gain.value; }
set depth(value) { this.#implementation.depthNode.gain.value = value; }

connect(audioParameter) {
this.#implementation.output.connect(audioParameter);
return this;
} //connect
disconnect() { this.#implementation.output.disconnect(); return this; }

deactivate() { this.#implementation.deactivate(); }

Auspicabilmente, questo codice si dovrebbe spiegare da solo. Gli insiemi di oscillatori


vengono utilizzati per implementare ModulatorSet, una classe base per le
classi Instrument e Tone.
Inizializzazione
Ci si potrebbe chiedere perché tutte le applicazioni del progetto Microtonal Music
Study inizino con il tasto "Start" sullo schermo o un pulsante di accensione.
Se si tenta di inizializzare il sottosistema Web Audio quando la pagina viene caricata,
fallirà. A seconda del browser, la produzione di suoni potrebbe essere ancora possibile
o richiederà il resume di AudioContext . In entrambi i casi, il primissimo suono andrà
perso, e potrebbe essere ritardato se l'inizializzazione viene eseguita pigramente alla
prima pressione di un tasto di uno strumento musicale. Pertanto, entrambe le pratiche
sarebbero sporche.
Web Audio inizia a funzionare completamente solo dopo che l'utente di una pagina
Web ha fornito un qualsiasi input su questa pagina.
Perché Web Audio si comporta in modo così strano
Penso che questo fatto sia molto buono. Cerca di proteggere gli utenti di una pagina
Web. Gli utenti si aspettano ragionevolmente un buon comportamento. Anche se molti
siti violano le buone pratiche, le persone generalmente non si aspettano che un bel sito
produca alcun suono senza l'esplicito consenso dell'utente. Immagina cosa può provare
un utente che a volte lavora nel cuore della notte se questa persona carica
accidentalmente una pagina Web, che potrebbe svegliare un'intera famiglia! E ogni
browser Web cerca di proteggere gli utenti da tali incidenti.
Pertanto, Web Audio dovrebbe essere inizializzato in anticipo, ma solo su alcuni input
degli utenti.
Per risolvere questo problema, viene utilizzato l'oggetto initializationController:
javascript
Riduci ▲
const initializationController = {
badJavaScriptEngine: () => {
if (!goodJavaScriptEngine) {
const title = `This application requires... better conforming to
standard`;
const advice =
`Browsers based on V8 engine are recommended, such as ` +
`Chromium, Chrome, Opera, Vivaldi, ...`;
document.body.style.padding = "1em";
document.body.innerHTML = //...
return true;
} //goodJavaScriptEngine
}, //badJavaScriptEngine
initialize: function (hiddenControls, startControl, startControlParent,
startHandler) {
for (let control of hiddenControls) {
const style = window.getComputedStyle(control);
const display = style.getPropertyValue("display");
this.hiddenControlMap.set(control, display);
control.style.display = "none";
} //loop
startControl.focus();
const restore = () => {
startControlParent.style.display = "none";
for (let control of hiddenControls) {
control.style.visibility = "visible";
control.style.display = this.hiddenControlMap.get(control);
} //loop
}; //restore
startControl.onclick = event => {
document.body.style.cursor = "wait";
startHandler();
restore();
document.body.style.cursor = "auto";
} //startControl.onclick
}, //initialize
hiddenControlMap: new Map()
}; //initializationController

Questo oggetto funziona abbastanza delicatamente con i controlli di pagina. Il


suo metodo initialize accetta un array di elementi da nascondere, un elemento
utilizzato come input per il comando di inizializzazione, il suo elemento padre e il
metodo di inizializzazione denominato startHandler. Durante la fase iniziale, gli
elementi da nascondere vengono nascosti, ma i loro valori di proprietà style.display
vengono memorizzati per essere poi ripristinati al termine dell'inizializzazione. Tutto il
resto si spiega da sé.
Notare la parte goodJavaScriptEngine. Questo viene fatto per filtrare i motori
JavaScript obsoleti, quelli che non supportano i membri della classe
privata. Sfortunatamente Mozilla non lo supporta, ma questo è utile solo per questa
applicazione, a causa dei problemi nella riproduzione del suono di Mozilla Gecko.
Vedere anche la sezione sulla compatibilità .

Compatibilità
Lo strumento è testato su un buon numero di browser e alcuni sistemi diversi.
Ho scoperto che al momento in cui scrivo esiste un solo tipo di browser con un
supporto sufficiente dell'API Web Audio: quelli basati sulla combinazione di
motori V8 + Blink . Fortunatamente, il set di tali browser è piuttosto ampio.
Sfortunatamente, i browser Mozilla, implementando formalmente l'API Web Audio
completa, producono un rumore scoppiettante piuttosto brutto che non può essere
eliminato, nella situazione in cui il funzionamento della combinazione V8 + Blink va
bene. Per il momento, l'applicazione impedisce l'utilizzo di questi e di alcuni altri
browser.
Ecco il testo delle raccomandazioni che una pagina mostra quando un browser non è in
grado di far fronte all'attività:
Questa applicazione richiede un motore JavaScript meglio conforme allo standard.
Si consigliano browser basati sul motore V8, come Chromium, Chrome, Opera, Vivaldi,
Microsoft Edge v. 80.0.361.111 o successivi, e altri...
A proposito, le mie congratulazioni alla gente di Microsoft, per la loro virtù di
rinunciare al praticamente defunto EdgeHTML utilizzato per Edge fino al 2020. :-)

Gioco dal vivo


L'applicazione può essere riprodotta su questa pagina del sito Microtonal Fabric . In
questo sito si possono trovare diverse applicazioni per tastiere musicali basate su
Sound Builder e alcuni strumenti sintetizzati creati con Sound Builder. Tutti possono
anche essere riprodotti dal vivo sulle loro pagine Web.
Inoltre, è possibile suonare diversi strumenti musicali (microtonali) basati sui dati creati
con SoundBuilder. Si prega di consultare la sezione "Applicazioni Live-Play"
nella pagina della documentazione principale di Microtonal Fabric .
Ancora una volta, non viene utilizzata alcuna parte server e nessuna rete, quindi
l'applicazione può essere scaricata e utilizzata su un sistema locale.
Molto probabilmente, per iniziare, potrebbe essere necessario un set di file di dati di
esempio, che possono essere scaricati da questa pagina dell'articolo.

Crediti
Wave FFT utilizza l’implementazione C# di Fast Fourier Transform di Chris Lomont .
Valeri Brainin , un eminente musicologo, manager musicale, compositore e poeta,
autore del famoso sistema pedagogico chiamato Metodo Brainin , ha partecipato
al progetto Microtonal Music Study come primo autore dell'ideazione, inventore o
coautore di alcuni progetti di tastiere microtonali , recentemente implementato sulla
base di Sound Builder. Ha iniziato a utilizzare quelle tastiere nella sua pratica
pedagogica e ha riportato risultati molto promettenti. Ha anche effettuato molti test
sulle prestazioni degli strumenti e sulla valutazione del suono durante lo sviluppo,
fornendo feedback che mi hanno aiutato a risolvere alcuni dei problemi e alcune idee
che sono state implementate o saranno implementate nelle versioni future.

Conclusioni
Ora funziona e penso che questo esperimento con la sintesi del suono musicale possa
essere definito un successo. Apprezzerò molto se qualcuno prova l'operazione e mi dà
un feedback; Sarò anche molto grato per qualsiasi domanda, commento, suggerimento
e soprattutto per le critiche.

Potrebbero piacerti anche