Upload
others
View
2
Download
0
Embed Size (px)
Citation preview
WebSocket Symphony Orchestra
Studente/i
Stefano Motti
Relatore
Nicola Rizzo
Correlatore
Giancarlo Corti
Committente
Nicola Rizzo
Corso di laurea
Ingegneria informatica
Modulo
M00002 Progetto di diploma
Anno
2018
Data
11 dicembre 2018
i
Indice
Abstract 1
Progetto Assegnato 3
Introduzione 5
1 Stato dell’arte 7
2 Struttura generale 9
2.1 Producer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.2 Dispatcher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3 Consumer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3 Metodologia di sviluppo 13
4 Producer 15
4.1 Connessione al dispatcher . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.1.1 Gestione touch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
4.2 Produzione singola nota . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
4.2.1 Stato della connessione con il server . . . . . . . . . . . . . . . . . . . 18
4.3 Selezione nota musicale e strumento . . . . . . . . . . . . . . . . . . . . . . 19
4.3.1 Stabilire connessione . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
4.3.2 Selezionare nota . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
4.3.3 Selezionare strumento . . . . . . . . . . . . . . . . . . . . . . . . . . 20
4.3.4 Controllo lunghezza nota con touch . . . . . . . . . . . . . . . . . . . 20
4.3.5 Sensore di prossimità . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
4.4 Produzione di una melodia . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.4.1 IP server dinamico . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.4.2 Nuovo protocollo di comunicazione . . . . . . . . . . . . . . . . . . . . 24
4.5 Polifonia 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
4.5.1 Produzione melodia nota per nota . . . . . . . . . . . . . . . . . . . . 25
4.5.2 CarillonSocket con canone . . . . . . . . . . . . . . . . . . . . . . . . 26
WebSocket Symphony Orchestra
ii INDICE
4.5.3 Costruzione di un ID univoco . . . . . . . . . . . . . . . . . . . . . . . 26
4.5.4 Feedback dal consumer . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.6 Polifonia 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5 Dispatcher 29
5.1 Connessione con producer(s) . . . . . . . . . . . . . . . . . . . . . . . . . . 29
5.1.1 DispatcherLogger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
5.1.2 Classe WebSocketServer . . . . . . . . . . . . . . . . . . . . . . . . . 32
5.2 Comunicazione verso il consumer . . . . . . . . . . . . . . . . . . . . . . . . 33
5.2.1 Tabella dei producer collegati . . . . . . . . . . . . . . . . . . . . . . . 33
5.3 Comunicazione nota, strumento o comando . . . . . . . . . . . . . . . . . . . 34
5.4 Comunicazione melodia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
5.4.1 DispatcherEmitter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
5.4.2 ConsumerEmitter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
5.4.3 Comunicazione indirizzo IP al producer . . . . . . . . . . . . . . . . . 36
5.5 Comunicazione polifonia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
5.5.1 Comunicazione preloading . . . . . . . . . . . . . . . . . . . . . . . . 37
5.6 Deploy dispatcher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
6 Consumer 39
6.1 Introduzione a Tone.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
6.2 Riproduzione di una nota . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
6.2.1 Riproduzione nota nel consumer . . . . . . . . . . . . . . . . . . . . . 41
6.2.2 Visualizzazione stato della connessione con il dispatcher . . . . . . . . 41
6.2.3 Visualizzazione operazioni del consumer . . . . . . . . . . . . . . . . 42
6.3 Riproduzione nota con strumento selezionato . . . . . . . . . . . . . . . . . . 42
6.3.1 Selezione nota . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
6.3.2 Selezione strumento . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
6.4 Riproduzione melodia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
6.4.1 Riproduzione singola nota . . . . . . . . . . . . . . . . . . . . . . . . 45
6.4.2 Mappatura note e strumenti . . . . . . . . . . . . . . . . . . . . . . . . 45
6.5 Riproduzione polifonia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
6.6 Deploy consumer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
7 Conclusioni 47
WebSocket Symphony Orchestra
iii
Elenco delle figure
2.1 Architettura del progetto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.1 Processo di sviluppo software SCRUM . . . . . . . . . . . . . . . . . . . . . 13
4.1 Layout producer prodotto dall’IDE Android Studio . . . . . . . . . . . . . . . . 15
4.2 Panoramica SocketClient . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
4.3 Codice che gestisce i tentativi di connessione . . . . . . . . . . . . . . . . . . 18
4.4 Codice che gestisce gli eventi del sensore di prossimità . . . . . . . . . . . . 21
4.5 Spartito Fra Martino . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.6 Estratto dello spartito dell’Aria sulla quarta corda . . . . . . . . . . . . . . . . 27
5.1 Prima implementazione della classe ListenerServer . . . . . . . . . . . . . . 30
5.2 Prima implementazione del DispatcherLogger . . . . . . . . . . . . . . . . . . 31
5.3 Costruzione del SocketIOServer . . . . . . . . . . . . . . . . . . . . . . . . . 32
5.4 Metodo in WebSocketServer che comunica con il dispatcherLogger . . . . . . 32
5.5 Tabella che rappresenta i producer connessi . . . . . . . . . . . . . . . . . . 33
6.1 Meotodo che riproduce la nota . . . . . . . . . . . . . . . . . . . . . . . . . . 41
6.2 Logica del bottone che mostra lo stato della connessione tra consumer e
dispatcher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
6.3 Metodo che riproduce la nota . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
6.4 Metodo che riproduce una melodia . . . . . . . . . . . . . . . . . . . . . . . . 44
WebSocket Symphony Orchestra
iv ELENCO DELLE FIGURE
WebSocket Symphony Orchestra
1
Abstract
Il progetto presenta un architettura divisa in tre componenti principali, ognuno dei quali ha
un compito preciso all’interno del ciclo di vita dell’applicazione. L’utente utilizzando un’ap-
plicazione android per smartphone è in grado di produrre delle note musicali utilizzando il
touchscreen del device oppure il sensore di prossimità. Inoltre è in grado di produrre due
melodie: Fra Martino e l’Aria sulla quarta corda di Bach. Le note vengono trasmesse ad un
server centrale con il protocollo socket e dal server sono trasmesse ad una pagina web tra-
mite protocollo websocket. Nella pagina web le note sono riprodotte in tempo reale rispetto
alle azioni che l’utente compie sul suo smartphone. Il sistema supporta la connessione di
più dispositivi che producono note, in questo mdo si può ottenere l’effetto di un orchestra
dove gli strumenti sono dei device collegati alla rete locale.
WebSocket Symphony Orchestra
2 Abstract
WebSocket Symphony Orchestra
3
Progetto Assegnato
Descrizione
Il progetto prevede la scrittura di un server che, ricevuti in ingresso alcuni parametri distintivi,
permetta ai client connessi di richiedere l’esecuzione di note musicali specifiche.
I client potranno essere dispositivi mobili, tablet o altri oggetti in grado di connettersi al server
usando il protocollo websocket e potranno usare tutti i sensori disponibili (prossimità, tocco,
accelerometri...) per decidere quando effettuare le richieste.
Le note verranno eseguite, almeno in un primo momento, utilizzando le tecnologie web
disponibili nei browser di ultima generazione (Web Audio)
Compiti
Lo studente dovrà occuparsi della scrittura del server, dell’esecutore vero e proprio di suoni
e di almeno un client che faccia da strumento musicale.
La comunicazione sarà bidirezionale in modo da permettere l’esecuzione automatica in mo-
dalità "rullo di piano", con dei feedback anche sul client (per esempio in caso di tastiera
virtuale, i tasti dovranno apparire premuti nel momento dell’esecuzione)
Obbiettivi
Ottenere il sistema descritto in modo tale che la latenza sia tanto bassa in modo da permet-
tere l’esecuzione di almeno un motivo elementare.
Il server dovrà inoltre permettere l’esecuzione contemporanea di più note e quindi di polifo-
nia
WebSocket Symphony Orchestra
4 Progetto Assegnato
WebSocket Symphony Orchestra
5
Introduzione
Nel primo capitolo si fa un introduzione sulle tecnologie presenti al giorno d’oggi che pos-
sono essere utilizzate all’interno del progetto. Si analizzano le applicazioni che riproducono
canzoni e musica più in generale, si passa attraverso applicazioni web che permettono di
comporre melodie nota per nota fino ad arrivare a parlare degli strumenti di più basso livello
che permettono di riprodurre suoni. Infine viene definito il concetto di realtime che il progetto
deve rispettare e vengono analizzate, anche in questo caso, le tecnologie già presenti e che
probabilmente verranno utilizzate.
Nel secondo capitolo viene presentata la struttura generale del progetto, esso si svilup-
perà diviso in tre componenti principali: un producer che, usato dall’utente, ha il compito di
produrre note musicali e melodie. Un dispatcher che rappresenta l’elemento centrale dell’ar-
chitettura, quello che mette in comunicazione producer e consumer. E infine un consumer
che il componente che riproduce i suoni.
Nel terzo capitolo viene spiegata la metodologia scrum come metodologia per sviluppare il
progetto. Si presentano le caratteristiche principali e i punti forte che fanno preferire questa
ad altre metodologie.
Nel quarto capitolo si parla più nello specifico del componente producer. Si analizzano
tutte le funzionalità implementate durante lo sviluppo del software in ordine cronologico e le
classi, i metodi e i meccanismi creati per raggiungere tali funzionalità.
Nel quinto capitolo viene trattato lo sviluppo del componente dispatcher. Come nel caso del
producer vengono analizzate le funzionalità raggiunte durante lo sviluppo software in ordine
cronologico ponendo una particolare attenzione alle classi, ai metodi e ai meccanismi che
sono stati adoperati per raggiungere tali funzionalità.
Nel sesto capitolo l’attenzione si sposta sull’ultimo componente rimasto, il consumer. Ana-
logamente ai componenti precedenti si sono analizzate le funzionalità che sono state imple-
mentate e le funzioni più importanti. Si spiega anche la struttura messa in piedi per gestire
le dipendenze all’interno del componente.
Nell’ultimo capitolo vengono esposti i risultati ottenuti e vengono trattate le conclusioni
riguardo al progetto.
WebSocket Symphony Orchestra
6 Introduzione
WebSocket Symphony Orchestra
7
Capitolo 1
Stato dell’arte
Al giorno d’oggi, nel web si trovano molte applicazioni che permettono di produrre e riprodur-
re musica in generale, nel particolare queste danno la possibilità all’utente sia di riprodurre
canzoni o melodie intere, sia di produrre una singola nota, sia di comporre una propria me-
lodia nota per nota. Le maggiori applicazioni che danno la possibilità di ascoltare intere
melodie (Spotify, Google Play Music e Youtube in primis) prima di riprodurle devono scari-
care megabyte di dati. Approfondendo, invece, il tema di produrre note o melodie nel web si
trovano diversi strumenti che permettono di comporre melodie e che mettono a disposizione
strumenti ed effetti per ogni genere e gusto (Caustic 3 e GarageBand per citare i più famosi).
Queste sì permettono di produrre musica ma ad alto livello, e si tratta di applicazioni che
producono e riproducono note solo al proprio interno: cioè non usano un riproduttore ester-
no e non hanno bisogno del web per svolgere il proprio compito. Continuando la ricerca
di strumenti che permettano di produrre note musicali si trovano le cosiddette "Web Audio
API"; queste sono state concepite con lo scopo di colmare il vuoto che solo parzialmente
ha riempito il tag HTML <audio> grazie al quale si ha la possibilità di riprodurre un audio in
streaming o in locale. Le Web Audio API sono relativamente giovani nel web: la prima rac-
comandazione a riguardo, scritta dal W3C (World Wide Web Consortium), è datata 20111
ma è solo dal 2015 che tutti i browser le supportano2. Nello specifico le Web Audio API
sono delle Application Programming Interface JavaScript di basso livello per l’elaborazione
e la sintetizzazione di file audio in applicazioni web. Queste API permettono di manipolare
file audio esistenti o qualsiasi sorgente sonora e di produrre un suono da zero. Per fare
ciò utilizzano degli oggetti detti "AudioNode" che sono utilizzati principalmente per tre scopi:
produrre, filtrare o riprodurre un suono; questi AudioNode possono essere combinati in qual-
siasi modo per ottenere il risultato desiderato. Nel caso del progetto "Websocket Symphony
Orchestra" non è stato necessario utilizzare delle API di così basso livello ma si è optato
per un framework come Tone.js3 che fornisce funzionalità di scheduling avanzato, sintetiz-
1https://www.w3.org/TR/webaudio/2https://caniuse.com/#searchw̄eb%20audio3https://github.com/Tonejs/Tone.js
WebSocket Symphony Orchestra
8 Stato dell’arte
zatori e effetti, e astrazioni musicali intuitive basate sulle Web Audio API. Questa scelta è
stata fatta anche perché la sua architettura è pensata per essere familiare sia ai musicisti
che ai programmatori audio. Tone.js offre funzioni DAW (Digital Audio Workstation) comu-
ni come un trasporto globale per lo scheduling di eventi, sintetizzatori e effetti predefiniti;
per i programmatori offre invece blocchi ad alte prestazioni e bassa latenza, e moduli DSP
per la costruzione di propri sintetizzatori, effetti o segnali di controllo complessi. Oltre alla
produzione di suoni il progetto prevede che un sistema distribuito lavori in realtime: cioè
che un componente qualsiasi dell’applicazione risponda agli eventi creati dagli altri appena
l’evento è scatenato. In questo caso quando l’utente produce una nota con un device la
nota è riprodotta immediatamente dal riproduttore collegato. Qualcosa di simile è stato fatto
in alcuni giochi per console come "Guitar Hero" in cui il giocatore utilizzava un joystick a
forma di chitarra e il suo scopo era schiacciare il pulsante giusto nel momento giusto per
suonare correttamente la canzone. La limitazione con questo strumento è lo scarso numero
di note (massimo 5) e strumenti (è stata prodotta un’altra versione che permette di usare
una batteria) a disposizione. Quindi per ottenere l’effetto di realtime nel nostro sistema si ha
bisogno di mezzi di comunicazione che assicurino bassa latenza e alte prestazioni. In que-
sta direzione si muove il protocollo di comunicazione Websocket, protocollo standardizzato
dall’IETF come RFC64554 e le sua API sono standardizzate dal consorzio W3C. Questa
tecnologia web fornisce canali di comunicazione full-duplex attraverso un singola connes-
sione con protocollo TCP (Trasmission Control Protocol): ciò significa che posso stabilire tra
due macchine una connessione tramite un singolo canale di comunicazione e che attraver-
so quest’ultimo si può comunicare in entrambe le direzioni con la sicurezza che i dati inviati
siano consegnati integralmente grazie al protocollo TCP. All’interno di questo contesto ho
scelto "Websocket Symphony Orchestra" come lavoro di tesi perché si tratta di un progetto
che parla di musica, argomento del quale sono molto appassionato e che vivo quotidiana-
mente come semplice ascoltatore, e comunicazione realtime nel web, un argomento che
associato alla musica mi ha affascinato per le potenzialità che potrebbero essere scoperte.
4https://it.wikipedia.org/wiki/WebSocket
WebSocket Symphony Orchestra
9
Capitolo 2
Struttura generale
Il progetto sin dall’inizio è stato suddiviso in tre parti indipendenti l’una dall’altra: così facen-
do la progettazione è avanzata parallelamente su tutte e tre le parti. La struttura prende
spunto dal Producer-Consumer pattern. Quest’ultimo descrive un’architettura nella quale
troviamo a monte un "producer", il quale ha il compito di produrre dati, nel mezzo un buffer,
nel quale vengono depositati i dati in attesa di essere processati, e a valle un "consumer", il
quale deve processare i dati contenuti nel buffer. Come è facile intuire in questa architettura
posso avere un numero indefinito di producer, con l’unica condizione di non produrre dati se
il buffer è pieno, e un numero indefinito di consumer, con la condizione che non richiedano
dati se il buffer è vuoto. Da questa struttura base si è costruita l’architettura del progetto:
come producer si è creata un’applicazione android che fosse in grado di collegarsi ad un
server e comunicare i dati che è capace di produrre; come consumer si è creata una pagina
web in grado di processare i dati in arrivo. Al posto di avere un buffer che immagazzina dati
troviamo un server che ha il solo compito di trasmettere i dati in arrivo dal/i producer verso
il/i consumer collegati.
Figura 2.1: Architettura del progetto
WebSocket Symphony Orchestra
10 Struttura generale
2.1 Producer
Nello sviluppo del progetto è stato necessario sviluppare un’applicazione android che fun-
gesse da producer. Ma andando con ordine elenchiamo le funzionalità che ha questa
applicazione:
• stabilire un collegamento con il server/dispatcher
• produrre dei dati ricavandoli dai sensori presenti (touch, prossimità, accelerometro,
...)
• produrre un motivetto elementare come un "rullo di piano"
• inviare i dati secondo un protocollo leggibile al consumer
Ottenute queste funzionalità all’interno della architettura completa non c’è nessuna differen-
za tra un’applicazione android o ios o di qualsiasi altro sistema operativo per device mobili.
Si parla di device mobili e non solo di smartphone perché anche, per esempio, i tablet ri-
spondono ai requisiti richiesti. Ho scelto di sviluppare su android per un semplice motivo,
perché possiedo uno smartphone android e ho potuto utilizzarlo sin da subito, e sempre,
per testare il funzionamento dell’applicazione descritta.
2.2 Dispatcher
Il dispatcher è l’elemento centrale in questa architettura e ad esso sono richieste queste
funzionalità:
• supportare la connessione di più producer
• inoltrare i dati verso il/i consumer
• avere una bassa latenza nell’inoltrare i dati
• registrare le operazioni che svolge
Anche se mette in collegamento producer e consumer è indipendente dalle altre compo-
nenti: i messaggi che arrivano dal producer sono mostrati all’utente nella pagina di log delle
operazioni e inoltrati al consumer che esegue le operazioni scritte nel messaggio. Il dispat-
cher in realtà non si interessa dei dati che riceve e inoltra, ma si interessa di garantire le
massime performance nel sistema.
2.3 Consumer
Il consumer è la parte finale dell’architettura ed è quella che ci fa apprezzare il lavoro svolto
dalle altre componenti; da esso ci si aspetta la seguente funzionalità:
WebSocket Symphony Orchestra
11
• eseguire le operazioni secondo il tipo di dato ricevuto
Questo componente è relativamente semplice: il suo compito è di processare i dati che gli
vengono inviati dal dispatcher. In pratica a seconda del messaggio che riceve deve essere
in grado di riprodurre delle note musicali.
WebSocket Symphony Orchestra
12 Struttura generale
WebSocket Symphony Orchestra
13
Capitolo 3
Metodologia di sviluppo
Figura 3.1: Processo di sviluppo software SCRUM
Come metodologia di lavoro è stato usato il modello agile SCRUM. Questo permette di ge-
stire il ciclo di sviluppo del software in modo iterativo e incrementale. Così facendo un ciclo
di lavoro dura un periodo di tempo fissato che può variare da 1 settimana fino a un mese.
Questo ciclo è chiamato sprint. In questo progetto si è deciso che la durata di uno sprint
fosse di 2 settimane. Durante questo periodo gli sviluppatori implementano una o più fun-
zionalità utente, o anche user story, in modo da ottenere entro fine sprint una versione del
software potenzialmente completa e funzionante. Ogni user story ha un valore definito in
termini di story points che è deciso dal gruppo di lavoro, o team: gli story points rappre-
sentano la valutazione del tempo che, in teoria, uno sviluppatore impeiega per risolvere il
problema in relazione alla sua complessità. Inizialmente è impossibile dare una valutazione
precisa in questi termini perché ogni sviluppatore ha le proprie capacità, però già dopo i pri-
mi sprint il processo di valutazione diventa più preciso. Tutte le user story individuate sono
messe in un "contenitore", o product backlog, dal quale ad ogni sprint sono estratte e mes-
WebSocket Symphony Orchestra
14 Metodologia di sviluppo
se nello sprint backlog. Qui le user story sono suddivise ulteriormente in feature, e queste
in semplici toDo; questo modus operandi aiuta gli sviluppatori a realizzare (relativamente)
piccole porzioni di codice che messe insieme creano un software funzionate. Per mettere in
pratica questa metodologia è stata utilizzata la piattaforma di TI-EDU SCM, o Source code
management, nella quale è possibile impostare le caratteristiche del modello agile come la
durata degli sprint, definire il contenuto di product backlog e configurare il contenuto degli
sprint.
WebSocket Symphony Orchestra
15
Capitolo 4
Producer
Dopo aver introdotto la struttura del progetto in generale, ora si entra più nel dettaglio
analizzando le singole componenti per funzionalità acquisite durante l’implementazione del
componente.
Il compito principale di questo componente è quello di produrre delle note musicali, o me-
glio è quello di produrre dei messaggi che interpretati dal consumer riproducano delle note
musicali.
4.1 Connessione al dispatcher
La prima funzionalità implementata è stata quella di poter stabilire una connessione tra
producer e dispatcher. Per fare ciò innanzitutto è stato necessario creare un’applicazione
android basilare. Questa prima versione divideva lo schermo in due parti distinte:
• PARTE SUPERIORE, che è la parte interattiva dell’applicazione
• PARTE INFERIORE, che è la parte in cui visualizzare il log delle operazioni
Figura 4.1: Layout producer prodotto dall’IDE Android Studio
Per creare una connessione tra producer e dispather si è usata la classe "Socket", classe
messa a disposizione da java nel package "java.net". Prima di parlare di implementazione
WebSocket Symphony Orchestra
16 Producer
bisogna tenere conto che un socket è fondamentalmente una coppia di numeri che rap-
presentano le porte attraverso le quali due computer, che hanno i ruoli di client e server
(rispettivamente producer e dispatcher), comunicano. Per poter stabilire questa connes-
sione è necessario che il client conosca l’indirizzo IP1 del server e la porta2. In questo
momento dell’implementazione queste informazioni sono state scritte direttamente nel co-
dice sorgente ma è una scelta non funzionale perché ogni volta che si cambia rete bisogna
cambiare l’indirizzo IP. La porta del server che si è scelto usare per comunicare è la 9999,
mentre la porta del producer è scelta in modo automatico dal sistema operativo tra quel-
le disponibili al momento. Per utilizzare la connessione e contemporaneamente interagire
con l’applicazione è necessario utilizzare una classe che ha il compito di far comunicare il
client e il server e che non processi sul thread principale. Questa classe è stata chiama-
ta SocketClient ed estende AsyncTask<>. AsyncTask è un classe ausiliaria della classe
Thread che mette a disposizione android nel suo package android.os. Questa permette di
eseguire operazioni in background, cioè non sul thread della User Interface (UI), e pubbli-
care i propri risultati sulla UI senza doversi preoccupare di sincronizzazione o altri problemi
di programmazione multithreading. Estendendo questa classe si deve sovrascrivere il me-
todo doInBackground() nel quale si trova il codice che esegue il thread. L’altro metodo
sovrascritto è onProgressUpdate() il quale è l’unico modo per interagire con la UI, cioè
pubblicare i log delle operazioni svolte.
Figura 4.2: Panoramica SocketClient
A livello pratico il thread stabilisce la connessione (socket.connect()), iniziallizza i buffer
che il socket usa per leggere i messaggi dal server (BufferedReader in) e scrivere i messag-
gi per il server (PrintWriter out). Dopodiché legge il messaggio di benvenuto del server (in
questo modo si verifica che la connessione è avvenuta con successo) e inizia il ciclo while
1Etichetta numerica che identifica univocamente un dispositivo collegato a una rete informatica2Numero da 0 a 65535 che identifica la porta su cui è in ascolto il server. I valori da 0 a 1024 sono riservati
e non utilizzabili
WebSocket Symphony Orchestra
17
(while (!closed && message != null)) responsabile della comunicazione da client verso
server. Questo ciclo ha come parametri di controllo una variabile booleana che permette
di interrompere dalla UI e una condizione sulla stringa che deve mandare al server. Ogni
volta che si modifica questa stringa (sempre dalla UI) l’istanza della classe SocketClient
invia il messaggio al server. Quando l’applicazione termina l’esecuzione o l’utente decide
di sospendere la connessione l’istanza comunica al server che ha terminato l’esecuzione
(out.println(getTimeStamp() + "Background thread finished!")) e chiude il socket. In
questo momento dello sviluppo software la connessione viene aperta al momento dell’aper-
tura dell’applicazione stessa. Questo modo di procedere non è ottimale ma verrà modificato
nelle prossime iterazioni.
4.1.1 Gestione touch
Raggiunto questo punto è stata implementata la "callback" onTouchEvent, ovvero quel me-
todo che viene chiamato dal sistema operativo quando un determinato evento si è verificato.
Questa callback accetta come parametro un oggetto di tipo "MotionEvent" il quale contiene
tutte le caratteristiche del tocco sul schermo del device. Si deve tenere presente che ogni
volta che si tocca lo schermo di un qualsiasi device android non vengono prodotti, come
si potrebbe pensare semplicemente, solo gli eventi di inizio_tocco e fine_tocco, ma molti
altri eventi che inconsapevolmente produce l’utente al momento del tocco. Bisogna tenere
conto che lo schermo dei dispositivi odierni ha milioni di pixel e che nel momento in cui si
tocca lo schermo stesso si stanno toccando molti più pixel che uno solo. Inoltre con un
normale tocco vengono prodotti oltre agli eventi di inizio e fine, anche eventi che rappresen-
tano lo scorrimento del dito su più pixel; questo è molto utile nel caso in cui l’applicazione
deve saper riconoscere dei movimenti preimpostati (le cosiddette "gesture") per reagire di
conseguenza, come nel caso di giochi che associano a dei movimenti delle determinate
azioni. Ma questo non rappresenta la nostra situazione e quindi ci si è limitati a gestire
gli eventi ACTION_DOWN e ACTION_UP: questi rappresentano gli eventi che corrispondo al
momento in cui comincio a toccare lo schermo (ACTION_DOWN) e al momento in cui smetto
di toccarlo (ACTION_UP). A questo punto dell’implementazione, agli eventi appena descritti
l’applicazione eseguiva semplicemente dei log nella textview3 in modo da poter verificare
che fossero gestiti correttamente. La textview occupa la PARTE INFERIORE dello schermo e
si può riconoscerla sia dal titolo che dal colore di sfondo (giallo) che è stato utilizzato.
4.2 Produzione singola nota
Durante il secondo sprint è stato scelto di implementare la funzionalità di produrre una nota
utilizzando il sensore touch del device. Per fare ciò la callback che gestisce il tocco sullo
schermo è stata modificata per produrre il messaggio "L’utente ha toccato il dispositivo!!".3elemento del layout android predisposto a mostrare testo modificabile
WebSocket Symphony Orchestra
18 Producer
Questo messaggio viene settato in un campo dell’istanza di SocketClient che sta eseguen-
do in background e verrà inviato al server che a questo punto dello sviluppo interpreterà il
messaggio. Infine è stato aggiunto che ogni messaggio di log prodotto dal producer con-
tenesse il timestamp4 del momento in cui l’azione loggata fosse avvenuta: in questo modo
si può verificare la latenza della rete confrontando questo valore con quello prodotto sul
server, o sul consumer. La differenza tra i valori del timestamp è nell’ordine dei centesimi di
secondo.
4.2.1 Stato della connessione con il server
Si è deciso inoltre che è opportuno che il producer non si collegasse la server automatica-
mente all’avvio dell’applicazione ma che fosse l’utente a decidere quando connettersi e che
il producer visualizzi lo stato della connessione con il server. In questo modo è possibile
gestire più efficientemente la connessione e i problemi che possono crearsi. Per prima cosa
è stato creato un bottone visualizzabile nella pagina principale dell’applicazione che contie-
ne la stringa "OFF" ed è di colore rosso per comunicare all’utente che la connessione non
è attiva. A questo bottone è stata aggiunta la callback che risponde al tocco sul bottone:
questa funzione ha il compito di cercare di stabilire la connessione con il server. E inoltre è
stato aggiunto un pezzo di codice che evita di bloccare l’applicazione nel caso in cui il server
sia offline e l’applicazione cerchi continuamente di connettersi. Il codice riprotato in figura
Figura 4.3: Codice che gestisce i tentativi di connessione
4.3 è quello che gestisce i tentativi di connessione del producer al server. Viene sfruttato il
metodo connect() messo a disposizione dalla classe Socket che accetta come parametri
l’indirizzo del server, come InetSocketAddress, e opzionalmente un tempo massimo entro
il quale connettersi. Scaduto tale tempo il metodo produce un’eccezione che viene gestita
comunicando all’utente che è impossibile al momento connettersi e che verrà effettuato un
altro tentativo più tardi. É stato deciso un numero massimo di tentativi, o meglio un tempo
massimo entro il quale se la connessione non è stabilita si interrompono tutti i tentativi di
connessione: questo tempo è di un minuto (MAXtimeout = 60) e si effettuano tentativi ogni 5
secondi (Thread.sleep(5000) ). In caso che la connessione sia stata stabilita con successo4Valore numerico che rappresenta data e ora del momento in cui è calcolato
WebSocket Symphony Orchestra
19
il bottone cambierà la propria stringa in "ON" ed il suo colore in verde, e la sua funzione non
cercherà di stabilire la connessione ma di interromperla andando a modificare il valore del
campo closed in true.
4.3 Selezione nota musicale e strumento
Per il terzo sprint è stato deciso di poter dare all’utente la possibilità di selezionare quale
nota produrre e con quale strumento riprodurla. Per fare ciò è stato aggiunto all’applicazione
un menù che contenesse le azioni che un utente si aspetta di poter effettuare. Tali azioni
sono:
• stabilire una nuova connessione
• selezionare la nota musicale
• selezionare lo strumento musicale
4.3.1 Stabilire connessione
Per poter stabilire una connessione con il server a livello di codice non si sono fatte grosse
modifiche: il bottone con cui prima si faceva questa azione è stato deresponsabilizzato e ha
solo la funzione di visualizzare lo stato della connessione attraverso i colori rosso e verde.
La voce del menù invece crea un’istanza della classe SocketClient con la quale si stabilisce
la connessione con il server.
4.3.2 Selezionare nota
Questa voce del menù apre una lista di note dalla quale l’utente può selezionare la nota che
desidera. Le note presentate sono scritte con la notazione anglosassone: le note classiche
DO, RE, MI, FA SOL, LA e SI sono anche rappresentate dalle lettere, rispettivamente, C, D,
E, F, G, A e B. Oltre alle note cosiddette "naturali", cioè senza alterazioni, è possibile sele-
zionare le altre cinque note che completano un’ottava5, ovvero A#6, C#, D#, F# e G#. Infine
ad ogni nota è associato un numero che indica in quale ottava riprodurre la stessa, e le
ottave rappresentate sono 7 (0 - 6). A livello di codice questo è stato realizzato con l’aggiun-
ta di tre classi Note, NoteFragment e MyNoteRecyclerViewAdapter, che rispettivamente
rappresentano una singola nota nell’elenco, l’elenco delle note in un nuovo fragment7 e ge-
stiscono l’interazione tra il contenitore di elementi di una lista (NoteFragment) e gli elementi
contenuti (Note). Queste tre classi sono state prodotte dall’IDE con File->New->Fragment-
>Fragment(List) con un esempio funzionante composto da DummyItem. Il codice che è
5Intervallo di 8 note posizionate a frequenza diversa nella scala musicale6La diesis7Un comportamento o una porzione di UI nella Activity principale
WebSocket Symphony Orchestra
20 Producer
stato prodotto automaticamente evidenzia le parti in cui dev’essere modificato a seconda
delle esigenze degli oggetti da rappresentare. Dopo che è stata selezionata una nota viene
prodotto il messaggio: timestamp/origine/NOTE:nota. Per origine si intende la classe che
produce il timestamp, quindi la classe che che ha effettuato l’azione. Il messaggio prodotto
è passato all’istanza della classe SocketClient che lo invierà via socket al server.
4.3.3 Selezionare strumento
Analogamente a quello che è stato fatto per le note, sono state create le stesse classi
che gestiscono la selezione dello strumento; gli strumenti disponibili sono: Basso elettrico,
Fagotto, Violoncello, Clarinetto, Contrabbasso, Flauto, Corno francese, Chitarra acustica,
Chitarra elettrica, Chitarra acustica con corde di nylon, Armonio, Arpa, Organo, Piano, Sas-
sofono, Trombone, Tromba, Tuba, Violino e Xilofono. Questa volta il messaggio prodotto
contiene la strumento con questa notazione: "timestamp/origine/INSTRUMENT:strumento".
4.3.4 Controllo lunghezza nota con touch
Durante questa iterazione si è voluto anche raggiungere l’obiettivo di controllare la lunghez-
za di riproduzione delle note come succede con uno strumento reale in cui il musicista
decide per quanto tempo riprodurre la nota. L’idea di base è quella di utilizzare il touch
come un tasto che finché è premuto la nota viene riprodotta, mentre quando viene rila-
sciato la nota smette di essere riprodotta. Per fare ciò sono stati introdotti nuovi messaggi
che identificano queste azioni: TOUCH_START e TOUCH_END. Questi messaggi, che sono
prodotti rispettivamente quando inizio a toccare lo schermo e quando smetto di toccarlo,
sono inviati al server tramite l’istanza della classe SocketClient sempre con il protocollo
"timestamp/origine/TOUCH_START/END".
4.3.5 Sensore di prossimità
L’ultima funzionalità che è stata introdotta in questo terzo sprint è stata l’utilizzo del sensore
di prossimità per la produzione di note e per il controllo della lunghezza di riproduzione della
nota stessa. Innanzitutto bisogna tenere presente che il sensore di prossimità non è pre-
sente su tutti i dispositivi, anche perché è un componente hardware. Proprio perché si parla
di un compoente hardaware le diverse versioni posso funzionare in modo differente a se-
conda del tipo di tecnologie che sono state usate per implementarlo. Nella guida di Android
Developers8 si avvertono i programmatori dei due tipi di sensori di prossimità esistenti:
• sensore continuo, ovvero restituisce la distanza dal sensore entro un range
• sensore discreto, ovvero restituisce due valori numerici che equivalgono a true o false
8https://developer.android.com/guide/
WebSocket Symphony Orchestra
21
Dopo aver constatato che il sensore sul device utilizzato per il progetto è di tipo discreto e
che restituisce due valori numerici (0.0 e 8.0) che indicano se un oggetto è vicino o lontano
si è potuto procedere ad utilizzarlo per produrre delle note. Come è facile ipotizzare l’utilizzo
di questo tipo di sensore è molto simile al comportamento del touch: produco un messag-
gio che identifica quando cominciare a riprodurre la nota e uno per stopparla. Per poter
utilizzare il sensore, a livello di codice, bisogna che la MainActivity implementi l’interfaccia
SensorEventListener per poter catturare gli eventi prodotti dai sensori. Questa interfaccia
è generica, nel senso che è possibile ricevere eventi da qualsiasi sensore presente sul devi-
ce a patto che questi sensori siano ottenuti dal SensorManager e che vengano registrati per
ottenere gli eventi. Ciò a livello di codice si ottiene così: Nel metodo onSensorChanged si
Figura 4.4: Codice che gestisce gli eventi del sensore di prossimità
riceve l’evento che è prodotto dal sensore e da questo si può estrapolare che tipo di evento
è. In questo specifico caso si controlla la distanza che il sensore calcola e se è minore della
metà del massimo valore rilevabile (if (distance < (proximitySensor.getMaximumRange()
/ 2))) si produce il messaggio PROXIMITY_START, in caso contrario il messaggio prodotto è
PROXIMITY_STOP. Fare un controllo del genere evita di fare una distinzione tra i due tipi
di sensori di prossimità che si possono avere, riducendoli essenzialmente ad un sensore
discreto. I messaggi prodotti sono inviati come nel caso del touch.
WebSocket Symphony Orchestra
22 Producer
4.4 Produzione di una melodia
Nel quarto sprint è stato deciso di implementare la funzionalità "carillon", cioè di produrre
una melodia preimpostata. Prima di poter produrre una melodia preimpostata è necessario
trovare una melodia e tradurla in un linguaggio compatibile con il consumer che deve ripro-
durla. La melodia scelta è "Fra Martino" suonata con un pianoforte. Dallo spartito musicale
Figura 4.5: Spartito Fra Martino
si è passati al solfeggio delle note, cioè a leggere le note dallo spartito come DO-RE-MI
etc, fino alla traduzione in note anglosassoni. Dopodiché ad ogni nota così tradotta è stata
aggiunta l’ottava in cui riprodurre la nota sotto forma di numero da 0 a 6 (coerentemente
alla lista da cui si può selezionare la nota singola). Infine ad ogni nota è stato associato
per quanto tempo riprodurre la nota, anche questo parametro è espresso come numero se-
guendo questa regola: DURATA [1 = 4/4; 2 = 2/4; 4 = 1/4; 8 = 1/8; 16 = 1/16; ...] Il numero
prima dell’uguale è quello che identifica la durata all’interno dell’applicazione, il rapporto a
destra dell’uguale indica il rapporto della durata della nota riferito al metro con cui è scritto
lo spartito. Con questo modo di leggere uno spartito , lo spartito di "Fra Marino" è stato
tradotto e salvato in un file:
INSTRUMENT:piano
MELODY:C3-4;D3-4;E3-4;C3-4;C3-4;D3-4;E3-4;C3-4;E3-4;F3-4;G3-2;E3-4;F3-4;G3-2;G3-8;A3-
8;G3-8;F3-8;E3-4;C3-4;G3-8;A3-8;G3-8;F3-8;E3-4;C3-4;D3-4;G2-4;C3-2;D3-4;G2-4;C3-2
La prima riga di questo file riporta lo strumento con cui si vuole riprodurre la melodia,
la seconda riga riporta la serie di note con tutte le informazioni necessarie alla riprodu-
zione secondo il protocollo "NotaOttava-Durata" e sono separate le une dalle altre da un
punto e virgola. Il file contenente queste informazioni per poter essere letto dall’applica-
zione dev’essere messo in una cartella speciale chiamata "raw", all’interno della cartel-
la che contiene tutte le risorse dell’applicazione chiamata "res". Se non fosse in questa
posizione del fileSystem l’applicazione dovrebbe avere i permessi per poter leggere dalla
memoria del dispositivo e inoltre l’applicazione dovrebbe sapere dove si trova il file all’in-
terno della memoria. Invece, in questo modo, è possibili riferirsi al file attraverso il suo
WebSocket Symphony Orchestra
23
id e in più farsi dare una "stream" per poter leggere il file (InputStream inputStream =
res.openRawResource(R.raw.fra_martino)). Per semplificare il codice si è creata una
classe ad hoc per leggere questo tipo di file ed è stata chiamata CarillonFileReaderHel-
per. Questa classe espone un solo metodo (readFile(String fileName, Resources res)
che restituisce un oggetto della classe CarillonMessage. Questo tipo di oggetto possiede
tre campi:
• String fileName, in cui viene riportato il nome della melodia letta, è un parametro del
metodo
• String instrument, in cui è riportato lo strumento con il quale riprodurre la melodia
• String[] melody, in cui un elemento è "NotaOttava-Durata"
Questa classe espone oltre ai relativi "getter" e "setter" un metodo toJsonString() che scri-
ve una stringa nel formato JSON9. Questo perché la stringa in formato JSON è il messaggio
che viene inviato inviato al server sempre tramite l’istnaza della classe SocketClient. Que-
sta funzionalità è messa a disposizione dell’utente come nuova voce del menù: Produci
Carillon.
4.4.1 IP server dinamico
Usare un indirizzo IP scritto manualmente nel codice per connettersi con il server non è mai
stata un’idea funzionale, quindi è stato creato un meccanismo grazie al quale l’applicazio-
ne producer possa ottenere dinamicamente l’indirizzo IP del server. Questo "meccanismo"
sfrutta un altro protocollo di comunicazione in rete, ovvero il protocollo UDP10. Attraverso
questo l’applicazione chiede a tutti i dispositivi connessi alla rete (broadcasting), su una spe-
cifica porta, se sono il server che sta cercando; quando il server riceve il messaggio questo
risponde all’applicazione con un altro messaggio. Grazie a questo scambio di messaggi e al
protocollo UDP è possibile ottenere l’indirizzo IP del server. Questo meccanismo, così co-
me è stato spiegato, è messo in funzione dalla classe DiscoverServerIPThread. Quest’ul-
tima estende AsyncTask perché deve processare in background rispetto alla MainActivity,
e quando ottiene la risposta dal server restituisce un valore di ritorno che è l’IP del server.
Un’istanza di questa classe è creata quando viene selezionata la voce del menù "Nuova
connessione" e viene immediatamente "messa al lavoro". Appena termina l’esecuzione e
restituisce l’IP questo è utilizzato per stabilire la connessione con il server.
9JSON, acronimo di JavaScript Object Notation, è un formato adatto all’interscambio di dati fra applicazioniclient/server
10User Data Protocol
WebSocket Symphony Orchestra
24 Producer
4.4.2 Nuovo protocollo di comunicazione
Ricapitolando, i messaggi che vengono trasmessi al server sono di due tipi allo stato attuale
di implementazione:
• CarillonMessage, per trasmettere una melodia intera da riprodurre
• stringa contente "TOUCH_START/STOP" per produrre e fermare una nota usando il
touch
• stringa contente "PROXIMITY_START/STOP" per produrre e fermare una nota usan-
do il sensore di prossimità
A livello di server questo implica che è il server stesso che deve capire di quale genere di
messaggio si tratta e comportarsi di conseguenza. Quindi per facilitare il lavoro del server si
è deciso di cambiare il modo di comunicare dal producer al server. Si è deciso di creare una
struttura di comunicazione che fosse compatibile con i messaggi che attualmente vengono
prodotti dal producer e che liberi il server dall’elaborazione di tali messaggi. Per ottenere
ciò è stata introdotta una nuova classe Message che contiene i seguenti campi:
• String note, cioè una stringa che contiene la nota selezionata
• String instrument, cioè una stringa contente lo strumento selezionato
• String id, cioè una stringa contente l’id univoco dell’istanza di Message
• String command, cioè una stringa che contiene o "PLAY" o "STOP"
Inoltre questa classe espone "getter" e "setter" per ogni campo e il metodo toJsonString,
già descritto per la classe CarillonMessage. Utilizzando questa classe i messaggi prodotti
utilizzando il touch o il sensore di prossimità diventano, a livello di codice, lo stesso tipo di
messaggio. Nella pratica quando si seleziona una nota questa viene salvata nell’istanza
della classe Message, quando si seleziona uno strumento anch’esso è salvato nell’istanza
della classe Message, quando si tocca il touch o ci si al sensore di prossimità viene settato
il campo command con "PLAY" e il campo con un id univoco creato dalla combinazione del
MAC address11 del device più il timestamp e il messaggio trascritto in forma JSON viene
inviato al server. In modo analogo quando si smette di toccare il touch o ci si allontana
dal sensore di prossimità il campo command è settato con il valore "STOP" e nuovamente
inviato al server. Si noti che durante il ciclo di vita dell’applicazione quando si produce una
singola nota viene utilizzata un’unica istanza della classe Message, la quale è modificata
ogni volta che l’utente seleziona una nuova nota o un nuovo strumento, e ogni volta che
produce la nota e la ferma.
11Codice di 48 bit assegnato in modo univoco dal produttore ad ogni scheda di rete ethernet o wireless almondo
WebSocket Symphony Orchestra
25
4.5 Polifonia 1
A livello di producer ha poco senso parlare di polifonia se la si intende per il suo significa-
to di ascoltare più suoni diversi contemporaneamente, ma ha senso se la si intende come
produrre più di una nota nello stesso momento. Ma prima di discutere dell’implementazione
della polifonia nel producer è necessario parlare della produzione di una singola melodia:
così com’è l’implementazione il messaggio prodotto contiene l’intera melodia ed è preoccu-
pazione del consumer riprodurla ma non è il risultato che si vuole ottenere. In realtà l’unico
compito che dovrebbe eseguire il consumer è eseguire i comandi che vengono prodotti dal
producer e gli unici comandi che il producer può produrre sono "PLAY" e "STOP".
4.5.1 Produzione melodia nota per nota
A questo punto la logica di produzione di una melodia viene completamente stravolta per-
ché è il producer stesso che deve mandare i messaggi nel momento giusto seguendo la
melodia scritta nel file, cioè deve produrre il messaggio di "PLAY" di una nota e dopo il tem-
po che deve durare quella nota il messaggio di "STOP" e subito quello di "PLAY" della nota
successiva, e così via. Per fare ciò è stata scritta una nuova classe, CarillonSocket, il cui
compito è quello di produrre i messaggi necessari per riprodurre la melodia correttamente.
Questa estende AsyncTask, quindi si tratta di un thread che esegue in background le pro-
prie operazioni. A livello pratico ogni istanza di questa classe apre un nuovo socket verso il
server perché in questo modo si evita di dover dipendere da un solo socket per inviare tutti
i messaggi (ipoteticamente molti anche nello stesso momento se si pensa ad un’orchestra)
e quindi avere un traffico sulla rete distribuito su diversi canali di comunicazione. La scelta
di aprire un nuovo socket per comunicare una melodia al server si è resa necessaria dopo
aver testato empiricamente12 i risultati ottenuti con l’implementazione che utilizzava solo un
canale per inviare i messaggi; i test facevano notare come la soluzione con un solo canale
aperto producesse degli errori nella riproduzione e tra i vari motivi c’era il fatto che i mes-
saggi arrivassero nel momento sbagliato. Ciò era dovuto alla cooperazione di più thread
che parallelamente settavano il messaggio da inviare al server e non c’era nessuna assi-
curazione che il messaggio venisse mandato nel momento corretto. Con l’uso di un nuovo
socket per ogni melodia questo problema non sorge e gli unici responsabili di errori nella
produzione della melodia sono lo scheduler del sistema android che gestisce l’esecuzione
dei thread e il traffico della rete su cui il sistema comunica. Dopo aver aperto la nuova con-
nessione al server l’istanza di CarillonSocket all’interno di un ciclo estrae le informazioni
di ogni singola nota dall’oggetto CarillonMessage e crea un nuovo oggetto Message con
il comando "PLAY" per inviare le informazioni al server e riprodurre la nota. L’informazione
riguardante la durata della nota è utilizzata per "mettere a dormire" il thread in modo da
aspettare il tempo giusto prima di inviare lo stesso messaggio inviato precedentemente solo12Praticamente ascoltare la melodia riprodotta
WebSocket Symphony Orchestra
26 Producer
con il comando "STOP". La durata non è utilizzata così come si trova scritta ma viene elabo-
rata per trasformarla in un intervallo di tempo in secondi attraverso la funzione toSecond().
Al termine del ciclo il lavoro del thread termina chiudendo il socket che ha utilizzato.
4.5.2 CarillonSocket con canone
Per raggiungere l’obiettivo di produrre una polifonia in questo sprint si è deciso di pro-
durre "Fra Martino" con un canone: cioè di produrre due melodie identiche di cui la se-
conda inizia dopo un certo numero di note della prima. Questo risultato è ottenuto con
l’ultimo parametro che accetta il costruttore della classe CarillonSocket, ovvero il para-
metro che inizializza il campo canon e che rappresenta il numero di note dopo le quali
far partire la stessa melodia. Per produrre la seconda volta la stessa melodia viene det-
to alla MainActivity di istanziare e far partire una nuova istanza di CarillonSocket (con
canon = -1 per non ripetere ulteriormente la melodia). Il passaggio attraverso l’activity
principale dell’applicazione è necessario per il corretto funzionamento del sistema opera-
tivo android, altrimenti l’applicazione si arresta producendo un errore interno. Infatti an-
droid permette solo all’Activity principale di creare e far eseguire nuovi thread; inoltre sem-
pre android obbliga gli sviluppatori ad utilizzare "Esecutore" per eseguire più di due th-
read (uno è la MainActivity, l’altro è SocketClient) alla volta correttamente: carillonSoc-
ket.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR). Nello specifico TH-
READ_POOL_EXECUTOR è un contenitore di thread che permette l’esecuzione in paral-
lelo, al contrario di SERIAL_EXECUTOR che esegue in modo ordinato e seriale. L’utente
può utilizzare questa funzionalità selezionando dal menù la voce "Produci polifonia".
4.5.3 Costruzione di un ID univoco
Ogni nota prodotta per poter essere correttamente riprodotta dal consumer necessità di ave-
re un ID che sia univoco all’interno del sistema: fino a questo momento l’ID consiste in una
stringa che è costruita concatenando una stringa che contiene il MAC address del device
ad una stringa che contiene il timestamp. Questo però non è più sufficiente per assicurare
l’univocità dell’ID perché se si pensa che in questo momento ci sono due thread che stanno
eseguendo (idealmente) in parallelo e che stanno producendo la stessa melodia potrebbe
capitare di produrre due note nello stesso istante. Per evitare che si creino problemi nella
riproduzione è stata modificata la costruzione dell’ID: si tratta sempre di una stringa otte-
nuta concatenando clientNumber13, MAC address, timestamp e strumento con cui voglio
riprodurre la nota. Questa costruzione assicura che anche se due o più thread producono
la stessa nota nello stesso momento avranno comunque sia un ID diverso.
13numero progressivo che il server comunica ai client al momento della connessione
WebSocket Symphony Orchestra
27
4.5.4 Feedback dal consumer
In seguito ad alcune considerazioni riguardo al consumer e alle risorse che deve avere
a disposizione per poter eseguire senza errori i comandi che lo raggiungeranno è stato
introdotto un messaggio che è prodotto dal consumer e mandato al producer. Il messaggio
che arriva al consumer è Preloading terminato ed è il segnale che il sistema ha caricato
tutte le risorse necessarie e aspetta messaggi dal producer. Il producer prima dell’arrivo di
questo messaggio non è in grado di produrre alcuna nota per evitare errori nel consumer.
L’utente può sapere quando può produrre le note guardando il colore del bottone in alto a
sinistra dell’applicazione. Questo bottone prima aveva solo il compito di dare un feedback
sullo stato della connessione tra producer e server, ora il colore verde significa sia che la
connessione è stata stabilita, sia che il consumer è pronto per l’esecuzione.
4.6 Polifonia 2
Durante l’ultima iterazione si è deciso di introdurre una nuova melodia per verificare il cor-
retto funzionamento della polifonia sia a livello di producer che a livello di consumer. La
melodia scelta è "Aria sulla quarta corda", cioè la trasposizione del violinista tedesco Au-
gust Wilhelmj dell’Aria della terza Suite per orchestra in re maggiore di Bach (BWV 1068).
Come fatto per "Fra Martino" il primo passo è stato tradurre lo spartito in un file leggibile
Figura 4.6: Estratto dello spartito dell’Aria sulla quarta corda
e interpretabile dall’applicazione. Questa volta però le melodie sono 2 diverse e suonabili
con due strumenti diversi: la prima riga è suonata solitamente con un violino, la seconda
riportando note solitamente più basse è suonata con un violoncello. Inoltre in questo spar-
tito compaiono le "legature di valore", ovvero quei tratti che uniscono alcuno note, come nel
caso delle prime tre note nella prima riga dello spartito. Queste legature di valore significano
WebSocket Symphony Orchestra
28 Producer
che le note dovrebbero essere suonate come un’unica nota, cioè non dovrebbe sentirsi lo
stacco tra una e l’altra, ma all’interno del protocollo che si utilizza per tradurre lo spartito
non esiste nessun modo per tradurre questo tipo di informazione. L’unica alternativa sa-
rebbe quella di riprodurre un’unica nota con lunghezza uguale alla somma delle lunghezze
delle note legate, ma questo non è possibile perché si stanno utilizzando numeri interi per
rappresentare la lunghezza delle note. Quindi il file risulterà così:
INSTRUMENT1:violin
MELODY1:F#5-1;F#5-1;F#5-4;B5-8;G5-8;E5-8;D5-8;C#5-8;D5-8;C#5-2;B4-8;A4-4;A4-8;A5-
1;A5-8;F#5-8;C5-8;B4-8;E5-8;D#5-8;A5-8;G5-8;G5-1;G5-8;E5-8;B4-8;A4-8;D5-8;C#5-8;G5-
8;F#5-8;F#5-2;F#5-4;G#5-8;A5-8;D5-4;D5-16;E5-16;F#5-8;F#5-8;E5-8;E5-8;D5-8;C#5-8;B4-
8;B4-16;C#5-16;D5-8;D5-4;C#5-8;B4-8;A4-1
INSTRUMENT2:cello
MELODY2:D3-4;D4-4;C#4-4;C#3-4;B2-4;B3-4;A3-4;A2-4;G2-4;G3-4;G#3-4;G#2-4;A2-4;A3-
4;G3-4;G2-4;F#2-4;F#3-4;E3-4;E2-4;D#2-4;D#3-4;B2-4;B3-4;E2-4;E3-4;D3-4;D2-4;C#2-4;C#3-
4;A2-4;A3-4;D3-4;D4-4;C#4-4;C#3-4;B2-4;B3-4;G#3-4;E3-4;A3-4;D3-4;E3-4;E2-4;A2-8;B2-
8;C#3-8;D3-8;E3-8;G3-8;F#3-8;E3-8
Dopodiché è stata creata una nuova classe, PolyphonicFileReader, che ha il compito di
leggere il file ed estrarre le due melodie separate. Le singole melodie sono salvate in oggetti
di tipo CarillonMessagge e ordinati all’interno di un array. A questo punto, coerentemente
con quanto fatto prima, ogni melodia deve essere prodotta in un thread ma così facendo
sorge il problema che un thread comincia ad eseguire prima dell’altro. Per risolvere ciò è
stato utilizzato lo strumento di sincronizzazione messo a disposizione da java CyclicBarrier.
Quest’ultimo può essere paragonato all’arbitro della gara dei cento metri piani: mette ad
aspettare i thread (che sono gli sprinter) finché non sono tutti arrivati allo stesso punto
dell’esecuzione e questo punto li lascia eseguire. Il numero di thread che fa aspettare è un
parametro che viene passato al costruttore e come dice il nome questo oggetto può essere
riutilizzato più volte. I thread vengono fermati dopo aver concluso le operazioni preliminari
come stabilire la connessione, inizializzare i buffer di input e output e prima di cominciare la
produzione delle note. Tutto questo permette di produrre la melodia polifonica correttamente
(a meno di latenze sulla rete) metricamente parlando.
WebSocket Symphony Orchestra
29
Capitolo 5
Dispatcher
Il dispatcher in questa architettura è l’elemento centrale, quello che permette la comunica-
zione tra il producer e il consumer. La sua funzione principale è quella di inoltrare i messaggi
prodotti da uno o più producer e indirizzarli verso il consumer. Visto le sua funzionalità all’in-
terno del sistema questo componente deve assicurare alte performance in termini di velocità
di inoltro dei messaggi per permettere la definizione di sistema realtime. Si è scelto di im-
plementare questo componente con il linguaggio di programmazione Java principalmente
perché possiede tutti gli strumenti necessari per costruire questo genere di applicazione,
perché è stato il linguaggio di programmazione di riferimento durante il mio ciclo di studi
e perché garantisce buone performance riguardo alla comunicazione via socket. Analo-
gamente al producer, in questo capitolo si analizzeranno le funzionalità acquisite durante
l’implementazione.
5.1 Connessione con producer(s)
La prima funzionalità implementata è stata stabilire una connessione con un producer e
come naturale conseguenza la possibilità di supportare più connessioni con eventuali più
producer. Per fare ciò sono state create due classi: ListenerServer e ListenerThread. La
prima è la classe principale, quella che inizializza tutti gli oggetti e che permette di mettere
in funzione il server. Tra gli oggetti che inizializza il più importante è un ServerSocket che
è un’implementazione java di un server socket, il quale aspetta delle richieste in arrivo sul-
la rete sulla porta specificata al momento della costruzione (ServerSocket listener = new
ServerSocket(listenerPortNumber)). La porta specificata è la 9999 ed è la stessa che
il producer deve specificare quando vuole connettersi al server. Essenzialmente l’oggetto
listener ha solo il compito di aspettare richieste di connessione e lo fa attraverso il meto-
do accept(); questo metodo blocca l’esecuzione del programma finché non stabilisce una
connessione e restituisce il socket aperto attraverso il quale producer e dispatcher possono
comunicare. Il socket che ha ritornato è poi utilizzato per creare un’istanza di ListenerTh-
WebSocket Symphony Orchestra
30 Dispatcher
read, il quale come dice il nome è un thread che si mette in ascolto sul socket. Il ciclo di
vita di questo thread comprende una fase iniziale in cui inizializza i buffer di input e output
(BufferedReader e PrintWriter) per comunicare attraverso il socket e invia un messaggio
di benvenuto al producer per comunicare due informazioni: il numero progressivo che iden-
tifica il producer e che la connessione è stata stabilita con successo. Dopodiché il thread
entra in un ciclo while che ha il compito di ascoltare i messaggi che produce e invia il pro-
ducer. String input = in.readLine() è il modo in cui i messaggi vengono letti: se il valore
della stringa ottenuta è null oppure "." il ciclo si interrompe e il thread chiude la connessione
socket e termina la sua esecuzione. Si svolge questa doppia verifica sulla stringa perché
si vogliono intercettare il caso in cui la connessione è caduta (caso null) oppure il caso in
cui il producer voglia interrompere la comunicazione e quindi invia un messaggio specifi-
co (".") per comunicarlo al dispatcher. Con queste due classi è stato possibile creare un
server multithread che supportasse la connessione di più producer contemporaneamente.
Le modifiche al codice sono state minime: infatti è bastato introdurre un ciclo While(true)
che aspetta nuove connessioni, e quando un nuovo producer si collega viene istanziato un
nuovo ListenerThread.
Figura 5.1: Prima implementazione della classe ListenerServer
5.1.1 DispatcherLogger
In un server multithread è assolutamente necessario avere un sistema per controllare le
operazioni che sono state svolte: in questo modo si ha maggiormente sotto controllo il ser-
ver stesso ed possibile scoprire e trovare più velocemente eventuali errori. Il sistema di
controllo implementato è una pagina web statica in cui vengono stampate tutte le operazio-
ni che il server compie. Quindi si è reso necessario introdurre un nuovo componente nel
sistema che prende il nome di DispatcherLogger. Per fare ciò si è utilizzato il framework
javascript Vue.js: questo è dedicato alla realizzazione di interfacce web reattive che sfrutta-
WebSocket Symphony Orchestra
31
no il dual-binding tra modello e vista1. Una particolarità di Vue sta nel fatto che è possibile
programmare un componente in tutte le sue parti, cioè struttura (HTML), stile (CSS) e logica
(Javascript), nello stesso file. Queste componenti possono essere combinate e riutilizzate
allo scopo di creare applicazioni web. La scelta di utilizzare un framework del genere è stata
principalmente per la familiarità già acquisita durante un altro progetto; ma oltre a questo
mette a disposizione del programmatore un modo molto semplice per aggiornare i dati e
visualizzarli sulla pagina. Infatti grazie al data-binding, cioè al meccanismo che associa
Figura 5.2: Prima implementazione del DispatcherLogger
dati a elementi dell’interfaccia utente, è necessario solo aggiornare la variabile logger per
mostrare all’utente i log che arrivano dal dispatcher. All’interno del tag <script> si trova la
proprietà sockets: questa è un’implementazione di Socket.IO per Vue che rende molto più
semplice gestire le connessioni tramite websocket. Andando con ordine, Socket.IO è una
libreria javascript per applicazioni web realtime che permette una comunicazione bidirezio-
nale tra web client e server. Per utilizzare questa libreria è stata prima di tutto installata
nel progetto attraverso il package manager "npm"; a livello di codice bisogna inizializzare
una variabile che si mette in ascolto su una determinata porta (SocketIstance = socke-
tio(’http://localhost:5560’)). Infine è necessario dire a Vue di utilizzare quella variabile
(Vue.use(VueSocketIO, SocketIstance)) per permettere a tutti i componenti ".vue" di rice-
vere eventi tramite websocket. Socket.IO mette a disposizione del programmatore un siste-
ma di multiplexing grazie ai "namespace"2: cioè sulla stessa connessione socket è possibile
mettersi in ascolto solo di alcuni eventi che sono specificati da delle stringhe e questi eventi
1https://www.html.it/pag/63947/vue-js-unintroduzione/2https://github.com/socketio/socket.io-protocol
WebSocket Symphony Orchestra
32 Dispatcher
sono chiamati, appunto, namespace. Con questo meccanismo al server basta specificare il
namespace corretto per assicurarsi che i dati che vuole trasmettere sia correttamente instra-
dati. Inoltre Socket.IO permette di inviare qualsiasi tipo di dato: dai tipi primitivi agli oggetti
più complessi. Facendo riferimento alla figura 5.2 si è utilizzato il namespace dispatchLog
per ricevere e gestire gli eventi di log dal server: in questo caso il tipo di dato ricevuto è un
stringa ed è mostrata sull’interfaccia utente così come è stata composta lato dispatcher.
5.1.2 Classe WebSocketServer
Il DispatcherLogger nell’architettura di progetto ha il ruolo di web client, mentre il ruolo di
server lo ricopre la classe WebSocketServer all’interno del dispatcher. Questa classe ha
il compito di inviare i messaggi dal dispatcher ai web client che sono in ascolto tramite il
meccanismo dei namespace. La classe WebSocketServer è un contenitore dell’oggetto
SocketIOServer, un’implementazione java del server Socket.IO basata su "Netty". Netty
è un framework per applicazioni di rete event-driven asincroni per un rapido sviluppo del
protocollo server & client che mantiene delle performance elevate3. SocketIOServer neces-
sita di conoscere l’indirizzo sul quale aprire una connessione websocket: questo indirizzo è
costruito in un oggetto di tipo Configuration come si può vedere nella figura 5.3. La porta
sulla quale viene aperta la connessione websocket tra dispatcher e dispatcherLogger è la
5560 ed è specificata nella classe principale all’interno della variabile dispatcherLogger-
PortNumber. L’istanza della classe SocketIOServer così creata è passata come parametro
Figura 5.3: Costruzione del SocketIOServer
al costruttore della classe WebSocketServer; al momento della creazione, dopo aver ini-
zializzato i propri campi il server è fatto partire con il metodo start(). L’istanza della classe
WebSocketServer è passato come parametro al momento della costruzione di tutte le istan-
ze di ListenerThread perché è l’oggetto tramite il quale il dispatcher comunica con il proprio
dispatcherLogger. Per comunicare è stato scritto il metodo sendDispatchLog che accetta
Figura 5.4: Metodo in WebSocketServer che comunica con il dispatcherLogger
come parametro una stringa. Questo metodo emette sul websocket un evento con il name-
space "dispatchLog" e i dati trasmessi sono la stringa contenente il messaggio da loggare.
3https://netty.io/
WebSocket Symphony Orchestra
33
Tutti i metodi scritti in questa classe avranno una struttura simile al metodo riportato in figura
5.4, l’unica differenza è il tipo del parametro che accettano.
5.2 Comunicazione verso il consumer
Nel secondo sprint si è voluta implementare la funzionalità di riprodurre una nota preimpo-
stata: l’utente utilizzando il producer vuole poter ascoltare una nota musicale. Il dispatcher
in questa funzionalità ha il compito di inoltrare il messaggio che riceve attraverso il socket
aperto con il producer verso il consumer. Per comunicare con il consumer il dispatcher
apre una nuova connessione websocket utilizzando un’altra istanza della classe WebSoc-
ketServer: il numero di porta scelto per questo socket è 5570. Così come nel caso del
dispatcherLogger, questa istanza di WebSocketServer è passata come parametro al co-
struttore di ListenerThread ed è l’unico mezzo di comunicazione verso il consumer. Al-
l’interno del ciclo while che "ascolta" i messaggi del producer è stato scritto un controllo:
if(input.contains("L’utente ha toccato il dispositivo!!")). Se si verifica questa condizio-
ne viene fatta la chiamata consumerWSS.reproduceNote(). Questo metodo della classe
WebSocketServer emette un evento sulla connessione websocket verso il consumer usando
il namespace "screenTouch" e i dati trasmessi sono la stringa "C6"4.
5.2.1 Tabella dei producer collegati
In un sistema che mette in collegamento uno o più producer con un server si rende neces-
sario che il server sappia quali producer siano collegati e quali si siano scollegati. Per fare
ciò nel dispatcher è stata creata una lista che contiene tutti le istanze di ListenerThread
create. Ma per mostrare all’utente quali producer siano o meno connessi è stata aggiunta
nel dispatcherLogger una tabella che contiene il numero progressivo che identifica il pro-
ducer e un valore booleano che rappresenta lo stato della connessione (true = connesso,
false = disconnesso). La tabella è stata realizzata con Bootstrap-Vue. Bootstrap-Vue è un
Figura 5.5: Tabella che rappresenta i producer connessi
4Equivale al DO nella sesta ottava
WebSocket Symphony Orchestra
34 Dispatcher
progetto che permette di utilizzare Bootstrap con le funzionalità di data-binding che met-
te a disposizione Vue per i propri componenti. Questo progetto fornisce dei tag speciali,
molto simili a quelli che si è abituati a utilizzare con Bootstrap, che permettono, tra le al-
tre componenti, di creare una tabella configurandone stile e contenuti. La linea di codice
<b-table striped hover :items="items" :field="fieldss"></b-table> è quella che permet-
te di creare la tabella: la proprietà :field è quella che permette di specificare i titoli delle
colonne nella tabella mentre la proprietà :item è quella che permette di aggiungere nuove
righe nella tabella. Entrambe queste proprietà sono collegate a delle variabili che rispet-
tivamente contengono un array di stringhe che danno il titolo di ogni colonna nella tabella
(fieldss) e un array inizialmente vuoto che verrà riempito da oggetti JSON in cui la chiave
è il titolo della colonna e il valore è ciò che viene mostrato nella tabella (items). Grazie
a Vue è possibile modificare il valore di queste due variabili per modificare i valori visua-
lizzati sull’interfaccia utente. Nella classe ListenerServer quando un producer si collega
viene fatta la chiamata dispatcherWebSocketServer.addClient(clientNumber). Questo
metodo emette un evento sul websocket aperto con il dispatcherLogger con il namespa-
ce "newClient" e i dati trasmessi sono il numero progressivo che identifica il producer. Il
dispatcherLogger quando riceve questo messaggio aggiunge una nuova riga alla tabella:
this.items.push(ClientNumber: clientNumber, Connected: true). Quando un produ-
cer si disconnette è l’istanza di ListenerThread associata che fa la chiamata dispatcher-
WSS.sendToDispatcherClientDisconnected(clientNumber). Questo metodo emette un
evento sul websocket aperto con il dispatcherLogger con il namespace "clientDisconnected"
e i dati trasmessi sono il numero che identifica il producer. La chiamata a questo metodo è
l’ultima operazione che il listenerThread esegue prima di terminare l’esecuzione. Nel dispat-
cherLogger il numero serve per trovare l’elemento all’interno dell’array items e aggiornare
il valore associato alla chiave "Connected" in false.
5.3 Comunicazione nota, strumento o comando
Durante la terza iterazione è stata data la possibilità all’utente di poter selezionare quale
nota riprodurre e con quale strumento riprodurla. Il dispatcher in questa funzionalità ha
il compito di riconoscere quale genere di messaggio l’utente ha prodotto e inoltrare nel
modo corretto le informazioni al consumer. Il dispatcher deve riconoscere se nel messaggio
è contenuto la nota, lo strumento oppure un comando di riproduzione o di stop. Nella
classe ListenreThread, per non appesantire eccessivamente il codice all’interno del ciclo
while, è stato scritto un metodo che ha il compito di controllare il contenuto del messaggio.
Nel seguente elenco si descrivono il controllo e il metodo invocato sull’istanza della classe
WebSocketServer che ha stabilito la connessione websocket con il consumer:
• if (input.contains("INSTRUMENT:")), con il namespace "setInstrument" è inviata
una stringa contenente lo strumento selezionato
WebSocket Symphony Orchestra
35
• if (input.contains("NOTE:")), con il namespace "setNote" è inviata una stringa con-
tenente la nota selezionata
• if (input.contains("TOUCH_START") || input.contains("PROXIMITY_START")), con
il namespace "playDefaultNote" è inviata la stringa "PLAY"
• if (input.contains("TOUCH_END") || input.contains("PROXIMITY_STOP")), con il
namespace "stopNote" è inviata la stringa "STOP"
5.4 Comunicazione melodia
Nel quarto sprint all’utente è stata aggiunta la funzionalità di riprodurre una melodia preim-
postata. Il dispatcher in questa funzionalità ha il compito di comunicare al consumer i mes-
saggi che riceve dal producer e di loggare questi messaggi sul dispatcherLogger. Rispetto
all’iterazione precedente è stato introdotto un protocollo di comunicazione tra producer, di-
spatcher e consumer che evitasse al dispatcher il compito di riconoscere se il messaggio
contenesse una nota, uno strumento o un comando. Questo nuovo protocollo prevede che
il producer produca due tipi di messaggi:
• Message, contenente nota selezionata, strumento selezionato, ID che identifica la
nota riprodotta e comando "PLAY" o "STOP"
• CarillonMessage, contenente nome della melodia, strumento con cui riprodurla, ar-
ray i cui elementi sono coppie "NotaOttava-Durata"
Entrambi questi tipi di messaggi sono inviati in una stringa scritta in formato JSON. All’inter-
no del ciclo while che ascolta i messaggi del producer ogni messaggio prodotto dall’utente
è loggato sul dispatcherLogger tramite la classe DispatcherEmitter, e tranne il messag-
gio iniziale prodotto dal producer (if (!input.contains("Connected!!"))) ogni messaggio è
inoltrato al consumer usando la classe ConsumerEmitter.
5.4.1 DispatcherEmitter
DispatcherEmitter è una classe che implementa l’interfaccia Runnable, cioè è obbligata a
sovrascrivere il metodo run() che è il metodo eseguito all’interno di un thread. Al costruttore
sono passati come parametri una stringa contenente il messaggio da loggare e l’istanza
di WebSocketServer con il websocket aperto verso il dispatcherLogger. Il thread creato
con questa classe effettua la chiamata dispatcherWSS.sendDispatchLog(data) dove la
variabile data è un stringa ottenuta concatenando il timestamp e il messaggio passato nel
costruttore.
WebSocket Symphony Orchestra
36 Dispatcher
5.4.2 ConsumerEmitter
ConsumerEmitter, come la precedente, implementa l’interfaccia Runnable. La differenza
sta nel metodo run(): il messaggio prima di essere inviato è utilizzato per creare un oggetto
di tipo JSONObject. A seconda se il messaggio da inviare contenga una singola nota (Mes-
sage) o una melodia (CarillonMessage) vengono fatte le chiamate rispettivametne ai meto-
di consumerWSS.forwardObject(object) e consumerWSS.forwardCarillon(object). Il
primo metodo emette un evento sul websocket aperto verso il consumer con il namespa-
ce "forward" mentre il secondo emette un evento con il namespace "carillon". Entrambi
trasmettono l’oggetto di tipo JSONObject creato.
5.4.3 Comunicazione indirizzo IP al producer
Per implementare il meccanismo che comunica al producer che vuole collegarsi al dispat-
cher l’indirizzo IP del dispatcher stesso è stata creata una nuova classe: IPCommunicator.
Questa classe implementa l’interfaccia Runnable e l’unica istanza creata è creata nella clas-
se principale per poter eseguire il thread che si mette in ascolto su un socket aperto sulla
porta 8500 con il protocollo UDP. Il protocollo UDP è necessario perché permette di riceve-
re messaggi attraverso un socket che non specifica un IP: infatti si utilizza il meccanismo di
broadcasting. Cioè il producer collegato alla rete invia un messaggio a tutti gli indirizzi IP
sulla rete (broadcast) e l’istanza di IPCommunicator che resta in ascolto quando riceve un
messaggio controlla che arrivi da un producer e gli risponde. Grazie alla risposta del dispat-
cher il producer può ottenere l’indirizzo IP con cui aprire un socket usando il protocollo TCP.
A livello di codice l’esecuzione di questa classe consiste in un ciclo while che aspetta l’arrivo
di messaggi: ciò avviene con la chiamata al metodo socket.receive(packetReceived). La
variabile socket rappresenta il socket UDP che ascolta sulla porta 8500 e il metodo invo-
cato blocca l’esecuzione del programma finché un messaggio non è arrivato. Il messaggio
arrivato è immagazzinato nella variabile packetReceived di tipo DatagramPacket; questo
oggetto oltre a contenere un buffer da cui è possibile ottenere il messaggio contiene anche
l’indirizzo IP della sorgente del messaggio. Il messaggio è convertito in una stringa e vie-
ne fatto il controllo if (clientMessage.equals("SONO_UN_CLIENT")) per verificare che sia
stato un producer del progetto Websocket Symphony Orchestra e non un altro dispositivo
connesso alla rete. A questo messaggio il dispatcher risponde con una stringa che contie-
ne "SONO_IL_SERVER" ed è inviato al producer in un oggetto di tipo DatagramPacket in
modo che il producer possa ottenere l’indirizzo IP del dispatcher. Questo thread termina la
sua esecuzione quando termina l’esecuzione del dispatcher.
WebSocket Symphony Orchestra
37
5.5 Comunicazione polifonia
Nella quinta iterazione è stata aggiunta la funzionalità di riprodurre una melodia polifoni-
ca, cioè di riprodurre più di un suono nello stesso istante di tempo. Il dispatcher per ot-
tenere questa funzionalità ha il compito di inoltrare i messaggi che contengono le note
al consumer. Non sono state aggiunte nuove classi o scritti nuovi metodi per ottenere il
comportamento desiderato, anzi è stato semplificato ulteriormente il protocollo di comuni-
cazione. Ora l’unico messaggio che viene prodotto dall’utente è solo di tipo Message e
nella classe ConsumerEmitter è stato eliminato il controllo che riconosceva tra Message e
CarillonMessage.
5.5.1 Comunicazione preloading
A causa della libreria utilizzata nel consumer è necessario che gli strumenti per riprodurre
le note vengano caricati correttamente e questa operazione occupa per un po’ di tempo il
consumer. Quando questo caricamento è terminato il consumer invia un messaggio verso
il dispatcher che a sua volta lo inoltra al producer. Questo messaggio che si muove nel-
la direzione opposta rispetto a tutti gli altri è gestito all’interno del dispatcher con le classi
WebSocketServer e ListenerThread. Nella prima classe è stato aggiunto un metodo che
ascolta sulla connessione websocket con il consumer un evento con namespace "Preloa-
dingTerminato". Associato a questo evento è inviato un valore booleano che è utilizzato per
inizializzare un campo della classe: this.preloading = aBoolean. Nella seconda classe è
stato scritto un nuovo ciclo while (prima di quello che si mette in ascolto sul socket del pro-
ducer) che aspetta che il valore della variabile diventi true per poter comunicare al producer
che può cominciare a produrre messaggi.
5.6 Deploy dispatcher
Durante l’ultimo sprint la funzionalità introdotta è stata riprodurre una nuova melodia; il di-
spatcher è già in grado di svolgere questo compito dall’iterazione precedente. Invece, come
descritto nel capitolo Metodologia di sviluppo, l’applicazione in ogni momento deve poter
essere messa in produzione. Per fare questo l’applicazione non deve più dipendere dall’IDE
di sviluppo utilizzato ma deve poter essere eseguibile come un qualsiasi programma. Per
quanto riguarda il dispatcherLogger il suo codice è stato compilato in modo da ottenere un
file .HTML che ne descriva la struttura, un file .js che ne gestisca la logica, un file .css che
ne descriva lo stile e una cartella con le risorse utilizzate (immagini). Questi file sono stati
utilizzati per pubblicare la pagina web che è il dispatcherLogger in rete locale utilizzando
XAMPP. XAMPP è un software open-source sviluppato da Apache Friends che utilizza tra
le altre cose un Apache HTTP Server ed è capace di interpretare pagine web dinamiche5.5https://en.wikipedia.org/wiki/XAMPP
WebSocket Symphony Orchestra
38 Dispatcher
Il dispatcherLogger è raggiungibile all’indirizzo "localhost:8082/index1.html". Per quanto ri-
guarda il dispatcher è stato compilato il codice java ed è stato ottenuto un file .jar. Questo
file è eseguibile da linea di comando con java -jar nome_file.jar e mette in funzione il server
con tutte le funzionalità implementate.
WebSocket Symphony Orchestra
39
Capitolo 6
Consumer
Il consumer nell’architettura del progetto il componente finale, il suo compito è quello di ri-
produrre le note che l’utente ha prodotto utilizzando il producer. Questo componente è stato
implementato con una pagina web statica e le sue funzionalità sono state implementate
utilizzando principalmente il linguaggio di programmazione javascript. All’interno di questo
componente si è utilizzata la libreria Tone.js come libreria di alto livello per sfruttare le Web
Audio API. Come i componenti precedenti, in questo capitolo si analizzeranno le funzionalità
che sono state acquisite durante lo sviluppo del progetto.
6.1 Introduzione a Tone.js
Prima di parlare delle funzionalità del consumer è bene introdurre alcuni concetti che riguar-
dano la libreria sulla quale sono state implementate tali funzionalità: Tone.js. Tone mette a
disposizione del programmatore dei sintetizzatori artificiali per riprodurre i suoni all’interno di
una pagina web, e come esempio viene riportato l’unico sintetizzatore artificiale che è stato
utilizzato durante lo sviluppo del progetto: var synth = new Tone.Synth().toMaster(). Ana-
lizzando la linea di codice viene chiamato il costruttore dell’oggetto Synth che rappresenta
un sintetizzatore base con un singolo inviluppo e un singolo oscillatore. Questo oggetto per
poter riprodurre suoni deve essere collegato all’output sonoro principale e ciò è fatto con
la chiamata al metodo toMaster(). Ogni oggetto che riproduce suoni in Tone per poterli
riprodurre deve fare la chiamata al proprio metodo toMaster(). Con l’oggetto appena co-
struito è possibile riprodurre qualsiasi nota con una durata specificata con la chiamata al
metodo synth.triggerAttackRelease(’C4’, ’8n’). Questo metodo accetta come primo pa-
rametro la nota musicale espressa in notazione anglosassone (o con la frequenza) e come
secondo parametro la durata della nota. Questo parametro è esprimibile in secondi, in BPM
(Battiti Per Minuto), in Herz o in PPQ (Pulse Per Quarter). triggerAttackRelease è un me-
todo che in realtà unisce due altri metodi che servono uno per iniziare a riprodurre una nota
(triggerAttack(note)) e una per fermare la riproduzione di tale nota (triggerRelease(note)).
WebSocket Symphony Orchestra
40 Consumer
Questi due metodi possono essere separati e utilizzati in parti diverse del codice per irpro-
durre una nota di durata variabile per esempio. Tone inoltre permette di creare uno stru-
mento musicale personalizzato per riprodurre le note: cioè fornendo ad un oggetto di tipo
Instrument una configurazione specifica e dei campioni di suoni di uno strumento musicale
è possibile riprodurre le note musicali con una chitarra o un pianoforte o qualsiasi altro stru-
mento musicale. L’interfaccia messa a disposizione da Tone per la classe Instrument è del
tutto simile a quella di Synth: i metodi toMaster() e triggerAttackRelease() sono ancora
disponibili. Con questa breve introduzione è stato spiegato come è possibile utilizzare Tone
all’interno di una pagina web e i principali metodi che sono stati usati.
6.2 Riproduzione di una nota
Lo sviluppo del consumer è iniziato nel secondo sprint quando si è aggiunta la funzionalità
di riprodurre una singola nota preimpostata. In questa funzionalità il consumer ha il com-
pito di ricevere un messaggio dal dispatcher e riprodurre la nota contenuta nel messaggio
utilizzando il framework Tone.js. Questo componente è stato costruito utilizzando Webpack.
Webpack è un "module bundler" per applicazioni javascript, cioè ha la funzione di unire i
moduli che compongono un progetto in un singolo file nell’ordine corretto. Nella pratica si
occupa di gestire le dipendenze al posto del programmatore fornendogli molti vantaggi du-
rante lo sviluppo. Per funzionare Webpack necessità di un file di "entry point", cioè il primo
file da cui parte per risolvere le dipendenze in modo ricorsivo, e di informazioni riguardo
l’output1. Tali informazioni sono il nome del file di output e la cartella di destinazione. Que-
ste informazioni sono scritte in un file di configurazione chiamato "webpack.config.js". Per
utilizzare webpack è necessario installarlo con "npm" nella cartella del componente. Il co-
mando sulla command line per installarlo è: npm install webpack webpack-cli –save-dev.
Oltre a webpack è stata installata anche la commnad line di webpack in modo da avere la
possibilità di eseguire il progetto da linea di comando. Dopo l’installazione bisogna costruire
la struttura di cartelle e file che compongono il progetto: una cartella "dist" che rappresen-
ta la destinazione dei file compilati, una cartella "src" che contiene i file sorgenti javascript
compilabili, una cartella "style" che contiene i fogli di stile css e una cartella "risorse" che
contiene risorse come immagini e file audio utilizzati nell’applicazione web. Per costruire
questo genere di progetto ho seguito la guida ufficiale di webpack2. In questa iterazione
è stato creato il file HTML che rappresenta la struttura della pagina web e il file "index.js"
che rappresenta l’entry point. In quest’ultimo file viene aperto il websocket che ascolta sul-
la porta 5570: let socket = io(’http://localhost:5570’) dove la variabile io rappresenta il
socket.io-client importato in questa pagina. Come nel caso del dispatcherLogger anche
il consumer utilizza l’implementazione client di Socket.IO, e per riprodurre la nota musicale
1http://webappfromzero.com/2018/04/01/webpack-cose-e-come-funziona/2https://webpack.js.org/guides/getting-started/
WebSocket Symphony Orchestra
41
si mette in ascolto dell’evento con namespace "screenTouch" e si aspetta di ricevere co-
me dato una nota musicale scritta in notazione anglosassone (A,B,C,D,E,F o G) seguita
dall’ottava in cui riprodurla. Come si vede in figura 6.1, quando il messaggio che contie-
Figura 6.1: Meotodo che riproduce la nota
ne la nota inviato dal dispatcher arriva al consumer essa viene riprodotta per 3 secondi:
synth.triggerAttackRelease(note,3);
6.2.1 Riproduzione nota nel consumer
In questa iterazione è stato implementato anche un meccanismo che permette di riprodurre
una nota cliccando su un bottone all’interno della pagina web del consumer. Questa funzio-
nalità, successivamente eliminata, è stata implementata per familiarizzare con il framwork
Tone.js e per prendere confidenza con gli strumenti che mette a disposizione. Per fare ciò
sono stati creati dei bottoni nel file HTML con dei valori che rappresentano una nota musi-
cale. Nel file js associato alla pagina web, al momento della creazione, a questi bottoni sono
associate due funzioni, una per il click e una per il rilascio: la prima legge il valore del bot-
tone e riproduce la nota (synth.triggerAttack(e.target.textContent);) mentre la seconda
termina la riproduzione della nota (synth.triggerRelease();)
6.2.2 Visualizzazione stato della connessione con il dispatcher
Per visualizzare lo stato della connessione tra il dispatcher e il consumer è stato creato un
bottone nella pagina web. Questo bottone si colora di verde quando il dispatcher si collega
per la prima volta al consumer; quando la connessione termina, cioè il dispatcher è offline, il
colore del bottone diventa rosso. Questa logica è implementata nei metodi riportati in figura
6.2.
Figura 6.2: Logica del bottone che mostra lo stato della connessione tra consumer edispatcher
WebSocket Symphony Orchestra
42 Consumer
6.2.3 Visualizzazione operazioni del consumer
Così come per gli altri componenti nell’architettura anche per il consumer è stato implemen-
tato un modo per visualizzare le operazioni che sono svolte nel ciclo di vita del consumer
stesso. Per fare la pagina web è stata divisa in due colonne con il sistema di griglie di
Bootstrap e una di queste è stata predisposta a contenere i messaggi di log prodotti dal
consumer. Con una funzione chiamata logging(text) il consumer può stampare a schermo
le operazioni che svolge e l’utente può vedere se le operazioni svolte sono corrette.
6.3 Riproduzione nota con strumento selezionato
Nel terzo sprint la funzionalità introdotta è quella di poter riprodurre una nota con lo strumen-
to selezionato controllandone la durata con il producer. Il consumer in questa funzionalità ha
il compito di riprodurre e stoppare la nota che l’utente ha selezionato quando il messaggio,
rispettivamente di "PLAY" e "STOP", sono arrivati al consumer. Per questo motivo sul web-
socket aperto verso il dispatcher sono stati aggiunti due nuovi metodi che sono in ascolto
degli eventi associati, rispettivamente, ai namespace "playDefaultNote" e "stopNote". Il pri-
mo di questi quando il messaggio arriva controlla che nel consumer siano state inizializzate
le variabile che rappresentano la nota selezionata e lo strumento selezionato dall’utente. Se
entrambe le variabili sono inizializzate la nota è riprodotta continuamente finché il messag-
gio di stop non arriva. Se non è stata selezionata la nota ma solo lo strumento il consumer
riproduce la nota di default "C6" con lo strumento selezionato. Se nessuna delle due va-
riabili è inizializzata il consumer riproduce con l’oggetto Synth la nota di default "C6" per 1
secondo. Nella figura 6.3 si può vedere che per controllare la lunghezza della nota non sono
Figura 6.3: Metodo che riproduce la nota
stati utilizzati i metodi triggerAttack e triggerRelease ma è stato utilizzato un altro oggetto
di Tone: Transport. Questo oggetto permette di usare la funzione passata come parametro
nella chiamata a Tone.Trasport.scheduleRepeat() all’interno di un loop: in questo modo la
nota continua ad essere riprodotta finché quando arriva il messaggio di stop si fa la chiama-
WebSocket Symphony Orchestra
43
ta Tone.Transport.clear(id). Il parametro passato alla funzione contiene un Id univoco che
rappresenta la nota che sta riproducendo Transport all’interno del suo loop ed è necessario
salvare questa informazione per poter sospendere la riproduzione correttamente.
6.3.1 Selezione nota
Quando l’utente ha selezionato la nota il producer produce un messaggio che contiene
l’informazione che riguarda la nota. Questo messaggio è inviato al consumer dal dispatcher
sul websocket aperto associandolo al namespace "setNote". La funzione che è in ascolto di
questo evento mostra a schermo la nota scelta e salva il valore nella variabile noteSelected.
Questa variabile è utilizzata dal consumer per riprodurre una nota quando l’utente produce
il messaggio di play.
6.3.2 Selezione strumento
Quando l’utente ha selezionato lo strumento il producer produce un messagio che contiene
l’informazione che riguarda lo strumento. Questo messaggio è inviato al consumer sul web-
socket aperto con il dispatcher associandolo al namespace "setInstrument". La funzione che
è in ascolto di questo evento oltre a mostrare a schermo lo strumento selezionato, costrui-
sce un oggetto di tipo Tone.Sampler. Questo oggetto è un estensione di Tone.Instrument
ed è in grado di interpolare automaticamente tra un gruppo di campioni registrati. A livello
di codice nel momento della costruzione a questo oggetto sono passati come parametro dei
campioni sonori che rappresentano una nota riprodotta con un determinato strumento. Que-
sto oggetto che espone metodi come triggerAttackRelease(), quando deve riprodurre una
determinata nota che è nel gruppo di campioni sonori che possiede utilizza Tone.Player; se
invece non è tra questi re-intona3 un campione automaticamente e lo riproduce. Per utilizza-
re gli strumenti musicali è stato importato il progetto di github "tonejs-instrument"4. Questo
progetto consiste in un file javascript che fornisce allo sviluppatore l’oggetto SampleLibrary
con il quale poter facilmente caricare i campioni di un strumento e ottenere il riproduttore
di suoni usando come parametro il nome dello strumento stesso. Ma il lavoro maggiore in
questo progetto è stato registrare i campioni degli strumenti e raccoglierli in una cartella.
Sono stati raccolti campioni degli strumenti: Basso elettrico,Fagotto, Violoncello, Clarinetto,
Contrabbasso, Flauto, Corno francese, Chitarra acustica, Chitarra elettrica, Chitarra acusti-
ca con corde di nylon, Armonio, Arpa, Organo, Piano, Sassofono, Trombone, Tromba, Tuba,
Violino e Xilofono.
3repitching4https://github.com/nbrosowsky/tonejs-instruments
WebSocket Symphony Orchestra
44 Consumer
6.4 Riproduzione melodia
Durante la quarta iterazione è stata aggiunta la funzionalità di riprodurre una melodia pre-
impostata e il consumer in questa funzionalità ha il compito di riprodurre una melodia leg-
gendo le note da un messaggio e riproducendole con il metro corretto. Il messaggio da
cui deve leggere la melodia è un oggeto JSON le cui chiavi contengono il nome della me-
lodia (fileName), lo strumento con cui riprodurla (instrument) e un array di oggetti JSON
in cui ogni elemento è una nota è la sua durata. Questo messaggio è inviato tramite
websocket ed è associato all’evento con namespace "carillon". Nel metodo rappresen-
Figura 6.4: Metodo che riproduce una melodia
tato in figura 6.4 si può notare che dopo alcune operazioni preliminari che servono per
eliminare la melodia riprodotta precedentemente si fa la chiamata let ins = mapWrap-
per.getInstrumentValue(carillonMessage.instrument). Nella variabile ins è salvato lo
strumento con cui riprodurre la melodia e mapWrapper è, tra le altre cose, un contenito-
re degli strumenti utilizzabili nel consumer. Dopodiché con ciclo for le note e le rispettive
durate sono salvate in un altro che array che salva l’informazione dell’istante di tempo in
cui riprodurre la note. Quest’ultimo array è utilizzato per costruire un oggetto di tipo To-
ne.Part che è una collezione di oggetti di tipo Tone.Event, i quali possono essere riprodotti,
stoppati e ripetuti come se fossero un evento singolo. La riproduzione inizia con la chia-
mata part.start(0) e il parametro "0" indica il ritardo con il quale cominciare a riprodurre la
melodia, in questo caso subito.
WebSocket Symphony Orchestra
45
6.4.1 Riproduzione singola nota
In questo quarto sprint è stato cambiato il protocollo di comunicazione, ora al consumer
arrivano solo due tipi di messaggi: uno che contiene una melodia e uno che contiene una
singola nota. Entrambi i messaggi sono oggetti JSON dai quali in javascript è facile leggere
i valori delle chiavi. Per quanto riguarda il messaggio che contiene una singola nota questo
è inviato sul websocket aperto tra dispatcher e consumer ed è associato al namespace "for-
ward". L’oggetto JSON contiene le informazioni riguardo la nota da riprodurre (note), lo stru-
mento con cui riprodurla (instrument), l’id univoco che rappresenta la nota (id) e il comando
di play o stop. Il metodo che riceve questo messaggio prima di tutto distingue il comando
play dal comando stop. Nel primo caso viene ottenuto lo strumento in modo analogo alla
riproduzione di un melodia, vengono mostrati a schermo nota e strumento, l’id è salvata nel
contenitore mapWrapper con la chiamata mapWrapper.insertIdPair(messageObject.id,
instr) e la variabile instr, che rappresenta lo strumento, è utilizzata per riprodurre la nota
con triggerAttack. Nel caso stop viene ottenuto lo strumento associato all’id del messag-
gio dal contenitore mapWrapper e viene chiamato il metodo triggerRelease. La coppia
id:strumento è infine rimossa dal contenitore. Con questo meccanismo non è più il consu-
mer a controllare in modo diretto la durata della nota da riprodurre ma esegue solo i comandi
che sono prodotti dall’utente sul producer.
6.4.2 Mappatura note e strumenti
In seguito al cambiamento nel protocollo di comunicazione si è reso necessario che i cam-
pioni audio degli strumenti fossero caricati prima che qualsiasi tipo di messaggio arrivi al
consumer. Questa operazione è svolta nel metodo che gestisce il momento in cui la connes-
sione websocket tra dispatcher e consumer viene stabilita (socket.on(’connect’, function
() {...}): all’interno di un ciclo for che itera su un array di stringhe che rappresentano gli stru-
menti vengono costruiti tutti gli strumenti, vengono collegati al master output e salvati in una
mappa con la coppia chiave:valore composta da nome_strumento:Tone.Sampler; mapW-
rapper è il contenitore di questa mappa e di un’altra mappa che associa id_nota:Tone.Sampler.
La prima mappa serve al consumer per salvare lo strumento e utilizzarlo quando si vuole
riprodurre una nota. La seconda invece serve per associare l’id della nota che si sta ripro-
ducendo allo strumento che la riproduce: così facendo quando arriva il messaggio di stop il
consumer recupera lo strumento che sta riproducendo il suono e lo interrompe.
6.5 Riproduzione polifonia
Nel quinto sprint all’utente è stata data la funzionalità di riprodurre una melodia polifonica e
il consumer ha il compito di riprodurre e gestire più di una nota contemporaneamente. Per
fare ciò non si è dovuto scrivere nessuna riga di codice aggiuntiva perché il contenitore ma-
WebSocket Symphony Orchestra
46 Consumer
pWrapper associando l’id della nota allo strumento che la riproduce permette al consumer
di far partire e stoppare la nota corretta. Il problema della polifonia, per quanto riguarda
il consumer, è stato risolto inconsapevolmente già nell’iterazione precedente quando ogni
strumento inizializzato è collegato al master output.
6.6 Deploy consumer
Nella sesta iterazione, come nel caso del dispatcher, non è stata aggiunta alcuna funziona-
lità che interessasse il codice del consumer e, analogamente al dispatcherLogger, il codice
del consumer è stato compilato per pubblicare la pagina web in rete locale con XAMPP. Il
consumer è raggiungibile all’indirizzo "localhost:8082/index1.html".
WebSocket Symphony Orchestra
47
Capitolo 7
Conclusioni
L’applicazione sviluppata risponde a tutti i gli obbiettivi fissati nella scheda di progetto. Il
sistema allo stato attuale presenta una latenza tanto bassa da permettere di controllare in
realtime la durata di una nota e di riprodurre un melodia le cui note sono prodotte dall’utente
con uno smartphone collegato in rete locale. Inoltre è possibile riprodurre un melodia po-
lifonica, cioè nello stesso istante è possibile ascoltare due note suonate con due strumenti
diversi. Per quanto riguarda le tre componenti da implementare bisogna fare il collegamento
tra i nomi scritti nella scheda di progetto e i nomi utilizzati nello sviluppo dell’applicazione: il
server ha preso il nome di dispatcher, l’esecutore di suoni si chiama consumer e il client che
faccia da strumento musicale è chiamato producer. Queste componenti sono indipendenti
le une dalle altre: basti pensare che nell’iterazione 6 è stata aggiunta una nuova funzionalità
e due componenti su tre non hanno subito modifiche. Di seguito una breve descrizione dei
risultati ottenuti componente per componente:
• Dispatcher Il dispatcher è il componente centrale che mette in comunicazioni i pro-
ducer al consumer. É in grado di aprire connessioni con uno o più producer usando il
protocollo socket e si connette al consumer utilizzando il protocollo websocket.
• Consumer Il consumer è il componente che è responsabile di riprodurre i suoni e si
collega al dispatcher usando il protocollo websocket. Consiste in un’applicazione web
che interpreta i messaggi che riceve dal dispatcher per riprodurre una o più note con
diversi strumenti.
• Producer Il producer è componente che interpreta lo strumento musicale, l’utente
può riprodurre note musicali utilizzando un producer come strumento musicale. Es-
so si collega al dispatcher utilizzando il protocollo socket e nella pratica consiste in
un’applicazione sviluppata in android per smartphone.
La comunicazione tra le componenti è bidirezionale grazie ai protocolli socket e websoc-
ket; infatti sia per quanto riguarda la connessione tra producer e dispatcher quando le due
componenti aprono una connessione entrambi inviano dei messaggi all’altra componente.
WebSocket Symphony Orchestra
48 Conclusioni
Questo succede anche nella connessione tra consumer e dispatcher. Infine la durante il
ciclo di vita del sistema ci sono i messaggi che rappresentano la nota da riprodurre che ven-
gono prodotti dal producer e inviati al consumer, e c’è un messaggio che il consumer invia
al producer per permettergli di produrre le note. L’unico compito che non è stato portato a
termine è il feedback sul producer quando una nota è riprodotta.
WebSocket Symphony Orchestra
49
Bibliografia
https://www.w3.org/TR/webaudio/
https://caniuse.com/#search=web%20audio
https://github.com/Tonejs/Tone.js
https://it.wikipedia.org/wiki/WebSocket
https://developer.android.com/
https://www.html.it/pag/63947/vue-js-unintroduzione/
https://github.com/socketio/socket.io-protocol
https://netty.io/
https://en.wikipedia.org/wiki/XAMPP
http://webappfromzero.com/2018/04/01/webpack-cose-e-come-funziona/
https://webpack.js.org/guides/getting-started/
https://github.com/nbrosowsky/tonejs-instruments
https://developer.android.com/
https://tonejs.github.io/
https://socket.io/
https://www.wikihow.it/Leggere-la-Musica
WebSocket Symphony Orchestra