Wave

 ☗ 

Generazione audio stereo

Il seguente programma genera un file .wav stereo (44100kHz 16bit) di cinque secondi di durata, contenente un tono sinusoidale di 200Hz sul canale sinistro, e 1000Hz su quello destro. I due toni hanno un'ampiezza di 16000 punti su 65536 possibili.

Ogni campione audio è formato da due byte relativi al canale sinistro, seguiti da quelli del canale destro. I due byte di ciascun canale contengono un valore 16bit signed compreso tra -32768 e +32767 (il byte basso viene per primo, seguito dal byte alto).

I dati da passare a writeframes devono essere una stringa binaria in Python2 e un oggetto bytes in Python3 (struct.pack produce già l'oggetto corretto per ogni versione).

import wave
import struct
from math import sin, pi  

duration = 5  
freq1 = 200  
freq2 = 1000  

FRAMERATE = 44100
samples = int(duration * FRAMERATE)  
alphastep = 2 * pi / FRAMERATE  
data = []  
for sample in range(samples):  
    alpha = alphastep * sample      # angolo relativo al campione per una frequenza di 1Hz
    y = 8000 * sin(alpha * freq1)  
    data.append(int(round(y)))  
    y = 8000 * sin(alpha * freq2)  
    data.append(int(round(y)))  

my_wave = wave.open('test.wav', 'wb')  
my_wave.setnchannels(2)  
my_wave.setsampwidth(2)  
my_wave.setframerate(FRAMERATE)  
my_wave.setnframes(0)  
my_wave.setcomptype('NONE', 'NONE')  
my_wave.writeframes(struct.pack('h'*len(data), *data))  
my_wave.close()


Generazione audio polifonica.

Il codice seguente crea un file .wav mono (44100kHz 16bit) della durata di cinque secondi contenente la somma di tredici diverse frequenze. Ogni frequenza ha un'ampiezza di 4000 punti.

import wave
import struct
from math import sin, pi

FREQUENCES = [131, 141, 151, 241, 272, 282, 292, 302, 415, 433, 515, 653, 701]
duration = 5

FRAMERATE = 44100
samples = int(duration * FRAMERATE)  
alphastep = 2 * pi / FRAMERATE  
data = []
for sample in range(samples):
    alpha = alphastep * sample      # angolo relativo al campione per una frequenza di 1Hz
    y = 0.0    
    for frequence in FREQUENCES: 
        y += 2000 * sin(alpha * frequence)
    data.append(int(round(y)))  

my_wave = wave.open('test.wav', 'wb')  
my_wave.setnchannels(1)  
my_wave.setsampwidth(2)  
my_wave.setframerate(FRAMERATE)  
my_wave.setnframes(0)  
my_wave.setcomptype('NONE', 'NONE')  
my_wave.writeframes(struct.pack('h'*len(data), *data))  
my_wave.close()


DDS

L'algoritmo Direct Digital Synthesis permette di calcolare il valore dei campioni senza ricorrere all'aritmetica floating point, pertanto risulta ideale per essere implementato tramite microcontrollori o direttamente in hardware.

Il principio è quello di avere già pronti i campioni di una sinusoide in una tabella, e leggerla (tramite un clock a frequenza costante) con un indice che "scorre" su di essa (anche saltando dei valori) tanto più rapidamente quanto più alta è la frequenza finale che si vuole ottenere.

In pratica è sufficiente indirizzare la tabella usando i bit alti (o anche tutti) di un contatore (registro o accumulatore di fase) formato da una quantità arbitrariamente grande di bit. Quanto maggiori sono i bit del contatore tanto maggiore è la risoluzione, ottenibile nella frequenza generata.

Ad ogni impulso di clock al contatore va sommato un valore costante K (detto anche tuning word) ottenuto dalla seguente formula:

           2bit  *  FrequenzaDesiderata
K =  ----------------------------------------
             FrequenzaCampionamento

Ad esempio se abbiamo una frequenza di campionamento (framerate) di 44100Hz, usiamo un contatore a 16 bit, e vogliamo ottenere 1000Hz, il valore di K dovrà essere paria a: 1486.

Le frequenze generabili sono tutte multipli esatti della frequenza minima possibile data da:

               FrequenzaCampionamento
Fmin =  ----------------------------------------
                      2bit

Il valore di K è appunto il rapporto tra la frequenza desiderata e quella minima:

               FrequenzaDesiderata
K =   ----------------------------------------
                FrequenzaMinima

Siccome con 44100Hz di framerate e 16bit di contatore la Fmin vale 0.6729Hz, per ottenere 1000Hz K deve valere 1000 / 0.6729 = 1486.

Le dimensioni della tabella (wave table o lookup table) dipendono dal numero di bit usati per il suo indirizzamento, l'ideale sarebbe usare tanti bit quanti sono quelli del contatore, ma questo comporterebbe in pratica tabelle troppo grandi. Ad esempio usando i 6 bit di ordine più alto del contatore si può indirizzare una tabella di soli 64 elementi.

Tanto più grande è la tabella, tanto meno rumore di fase (quantizzazione di fase) viene introdotto, allo stesso modo quanti più sono i bit che formano ogni elemento della tabella, tanto meno rumore di ampiezza (quantizzazione di ampiezza) viene introdotto.

In pratica per non avere troppa distorsione, la massima frequenza generabile non dovrebbe superare un terzo della frequenza di campionamento (su un limite teorico di metà frequenza di campionamento), in pratica il valore di K dovrebbe sempre essere inferiore 0.33*2bit

Il codice seguente crea un file .wav mono (44100kHz 16bit) della durata di 2 secondi contenente un tono sinusoidale di 1000Hz ottenuto tramite algoritmo DDS. I dati grezzi vanno "ripuliti" dal rumore tramite un apposito filtro (che comunque non garantisce totalmente l'assenza di aliasing).

from math import sin, pi
import wave
import struct

LUT_LENGTH = 64
# Calcola lookup table con i campioni di sinusoide 16bit signed
lut = [int(8000 * sin(pi*2*x/LUT_LENGTH)) for x in range(LUT_LENGTH)]
FRAMERATE = 44100
k = 1486
DURATION = 2   # durata tono generato in secondi
data = []
samples = int(DURATION * FRAMERATE)
counter = 0
for _ in range(samples):
    data.append(lut[counter >> 10])
    counter = counter + k & 0xFFFF

# smooth triangolare, in hardware questa funzione
# viene svolta dal filtro passa basso dopo il convertitore DAC
for i in range(2, len(data)-3):
    data[i] = int(round( 
        (data[i-2] + (data[i-1]*2) + 3*data[i] + (data[i+1]*2) + data[i+2]) / 9 
    ))

my_wave = wave.open('test.wav', 'wb')  
my_wave.setnchannels(1)  
my_wave.setsampwidth(2)  
my_wave.setframerate(FRAMERATE)  
my_wave.setnframes(0)  
my_wave.setcomptype('NONE', 'NONE')  
my_wave.writeframes(struct.pack('h'*len(data), *data))  
my_wave.close()


DTMF con DDS

I segnali multifrequenza DTMF codificano sotto forma di doppia frequenza i simboli presenti sulle tastiere dei telefoni. La pressione di un tasto crea contemporaneamente una frequenza di riga e una frequenza di colonna come indicato nello schema seguente (le frequenze sono indicate in Hz):

 -----   -----   -----   -----
 | 1 |   | 2 |   | 3 |   | A |      697
 -----   -----   -----   -----

 -----   -----   -----   -----
 | 4 |   | 5 |   | 6 |   | B |      770
 -----   -----   -----   -----

 -----   -----   -----   -----
 | 7 |   | 8 |   | 9 |   | C |      852
 -----   -----   -----   -----

 -----   -----   -----   -----
 | * |   | 0 |   | # |   | D |      941
 -----   -----   -----   -----

   
  1209    1336    1477    1633
#------------------------------------------------------------------------------
# Esempio di generazione toni DTMF con algoritmo DDS - By C.Fin 2014
#------------------------------------------------------------------------------
from math import sin, pi
import wave
import struct

# Calcola due lookup tables con i campioni di sinusoide 16bit signed
# hlut ha un'ampiezza 28% maggiore (specifica DTMF)
AMP = 8000        # ampiezza semionda
LUT_LENGTH = 64   # numero di campioni in ogni lut
lut = [int(AMP * sin(pi*2*x/LUT_LENGTH)) for x in range(LUT_LENGTH)]
hlut = [int(1.28 * AMP * sin(pi*2*x/LUT_LENGTH)) for x in range(LUT_LENGTH)]

# Calcola le costanti K per ogni frequenza considerando di usare
# contatori a 16 bit (i 6 bit piu` significativi indirizzano le lut)
DTMF_ROWS = [697, 770, 852, 941]       # frequenze righe in Hz
DTMF_COLS = [1209, 1336, 1477, 1633]   # frequenze colonne in Hz
FC = 44100                             # frequenza campionamento
dtmf_krows = [int(round((freq << 16) / float(FC)) ) for freq in DTMF_ROWS]
dtmf_kcols = [int(round((freq << 16) / float(FC)) ) for freq in DTMF_COLS]


# Dizionario per associare ogni simbolo ad una coppia di coordinate
# che identificano le costanti K delle relative frequenze
SYMBOLS = {
    '1':(0,0),  '2':(0,1),  '3':(0,2),  'A':(0,3),
    '4':(1,0),  '5':(1,1),  '6':(1,2),  'B':(1,3),
    '7':(2,0),  '8':(2,1),  '9':(2,2),  'C':(2,3),
    '*':(3,0),  '0':(3,1),  '#':(3,2),  'D':(3,3)
    }

DURATION = 0.1   # durata toni e pause in secondi
data = []
for symbol in '*1234567890#ABCD':
    row, col = SYMBOLS[symbol]
    accum_row = accum_col = 0
    for _ in range(int(DURATION * FC)):
        data.append(lut[accum_row >> 10] + hlut[accum_col >> 10])
        accum_row = accum_row + dtmf_krows[row] & 0xFFFF
        accum_col = accum_col + dtmf_kcols[col] & 0xFFFF
    data.extend([0] * int(DURATION * FC))  # pausa tra toni

# smooth triangolare, in hardware questa funzione
# viene svolta dal filtro passa basso dopo il convertitore DAC
for i in range(2, len(data)-3):
    data[i] = int(round( 
        (data[i-2] + (data[i-1]*2) + 3*data[i] + (data[i+1]*2) + data[i+2]) / 9 
    ))

my_wave = wave.open('test.wav', 'wb')  
my_wave.setnchannels(1)  
my_wave.setsampwidth(2)  
my_wave.setframerate(FC)  
my_wave.setnframes(0)  
my_wave.setcomptype('NONE', 'NONE')  
my_wave.writeframes(struct.pack('h'*len(data), *data))  
my_wave.close()


Beep con pygame

Possiamo usare pygame per dare un minimo di sonorità ai nostri programmi (anche solo console). Di seguito un esempio di classe per generare dei semplici beep, o una sequenza di toni.

L'emissione sonora è volutamente bloccante, il programma prosegue solo alla fine di essa. Se non si vuole questo comportamento basta togliere il ciclo while running.

Se non si passano argomenti a beep (sempre una o più coppie frequenza/durata in Hz/secondi) viene per default generato un tono di 800 Hz per 0.25 secondi.

#------------------------------------------------------------------------------
# pygame beep - by C.Fin 2014 - Codice Python2
#------------------------------------------------------------------------------
from math import sin, pi
import struct
import io
import wave
import pygame
import time

class Beep:

    def __init__(self):
        self.FRAMERATE = 44100
        self.SONG_END = pygame.USEREVENT + 1
        self.channel = pygame.mixer.Channel(1)
        self.channel.set_endevent(self.SONG_END)

    def beep(self, *args):
        if not args: args = 800, 0.25
        data = []
        alphastep = 2 * pi / self.FRAMERATE  
        for i in range(0, len(args), 2):
            f, t = args[i], args[i+1]
            samples = int(t * self.FRAMERATE)
            for sample in range(samples):  
                alpha = alphastep * sample      # angolo per 1Hz
                y = 8000 * sin(alpha * f)  
                data.append(int(round(y)))  
        self.myBuffer = io.BytesIO()
        my_wave = wave.open(self.myBuffer, 'wb')  
        my_wave.setnchannels(1)  
        my_wave.setsampwidth(2)  
        my_wave.setframerate(self.FRAMERATE)  
        my_wave.setnframes(0)  
        my_wave.setcomptype('NONE', 'NONE')  
        my_wave.writeframes(struct.pack('h'*len(data), *data))  
        my_wave.close()
        self.myBuffer.seek(0)
        self.channel.play(pygame.mixer.Sound(self.myBuffer))
        running = True
        while running:
            time.sleep(0.005)
            for event in pygame.event.get():
                if event.type == self.SONG_END: 
                    running = False

pygame.init()
beep = Beep().beep

beep(2000, 0.4,  900, 0.05, 1000, 0.05, 2000, 0.05, 900, 0.05, 1000, 0.05, 
     2000, 0.05, 900, 0.05, 1000, 0.05, 2000, 0.05, 900, 0.05, 1000, 0.2,
     800, 0.25)

pygame.quit()

Se si vuole suonare un motivo è meglio inserire tutti i toni in una singola chiamata beep, altrimenti possono esserci delle pause aleatorie tra un beep e l'altro.

Se si suonano motivi ripetuti spesso, e magari molto lunghi, è il caso di prevedere all'interno della classe un'apposito dizionario di memoizzazione, in modo da non dover ricalcolare ogni volta i dati.

La memoizzazione è quella tecnica che prevede di salvare i risultati di un'elaborazione in modo tale che se vengono di nuovo forniti gli stessi parametri di calcolo si possa recuperare la soluzione già pronta.

In pratica è sufficiente un dizionario:

if args in memoize_dict:
    solution = memoize_dict[args]
else:
    ...calculate solution...
    memoize_dict[args] = solution


Le note musicali

Per suonare dei motivi "con le note giuste" e non usando frequenze a caso...

La comune scala musicale temperata occidentale prevede note la cui frequenza è sempre in rapporto costante rispetto alla nota precedente. Questo significa che la differenza in Hz tra le diverse note cresce esponenzialmente da una nota all'altra. Il rapporto vale esattamente: 12√2

Ad esempio partendo dal la dell'ottava centrale del pianoforte (440 Hz) otteniamo il si successivo (due semitoni più avanti) con:
440 * 12√2 * 12√2 = 440 * (12√2)2 = 493.88

Dodici note, dette semitoni, formano un'ottava. Ogni ottava si estende per un range di frequenze doppio rispetto alla precedente, ogni nota ha perciò una frequenza doppia rispetto alla corrispondente nota dell'ottava inferiore, e metà di quella dell'ottava superiore.

Ad esempio partendo dal la dell'ottava centrale del pianoforte (440 Hz) otteniamo il la successivo (dodici semitoni più avanti) con:
440 * (12√2)12 = 440 * 2 = 880

La frequenza 261.63 Hz è detta do centrale della tastiera del pianoforte. Un pianoforte a 88 tasti copre completamente sette ottave, più alcune note dell'ottava inferiore e una nota di quella superiore:

  4186.0                                                                                                       8
  2093.0   2217.5   2349.3   2489.0   2637.0   2793.8   2960.0   3136.0   3322.4   3520.0   3729.3   3951.1    7
  1046.5   1108.7   1174.7   1244.5   1318.5   1396.9   1480.0   1568.0   1661.2   1760.0   1864.7   1975.5    6
  523.25   554.37   587.33   622.25   659.26   698.46   739.99   783.99   830.61   880.00   932.33   987.77    5
 *261.63*  277.18   293.66   311.13   329.63   349.23   369.99   392.00   415.30   440.00   466.16   493.88    4
  130.81   138.59   146.83   155.56   164.81   174.61   185.00   196.00   207.65   220.00   233.08   246.94    3
  65.406   69.296   73.416   77.782   82.407   87.307   92.499   97.999   103.83   110.00   116.54   123.47    2
  32.703   34.648   36.708   38.891   41.203   43.654   46.249   48.999   51.913   55.000   58.270   61.735    1
                                                                                   27.500   29.135   30.868    0
|        |////////|        |////////|        |        |////////|        |////////|        |////////|        | 
|        |////////|        |////////|        |        |////////|        |////////|        |////////|        | 
|        |////////|        |////////|        |        |////////|        |////////|        |////////|        | 
|   C    |// C# //|   D    |// D# //|   E    |   F    |// F# //|   G    |// G# //|   A    |// A# //|   B    | 
|        |// D- //|        |// E- //|        |        |// G- //|        |// A- //|        |// B- //|        | 
|        |////////|        |////////|        |        |////////|        |////////|        |////////|        | 
|         '------'          '------'         |         '------'          '------'          '------'         | 
|             |                 |            |             |                 |                 |            | 
|             |                 |            |             |                 |                 |            | 
|             |                 |            |             |                 |                 |            | 
|     DO      |        RE       |     MI     |      FA     |       SOL       |       LA        |     SI     | 
|             |                 |            |             |                 |                 |            | 
|             |                 |            |             |                 |                 |            | 
 '-----------' '---------------' '----------' '-----------' '---------------' '---------------' '----------' 

I codici MIDI rappresentano 128 note disposte su undici ottave numerate da -1 a 9. Il codice 60 rappresenta il do centrale del pianoforte, come si vede un pianoforte può suonare solo un sottoinsieme di tutte le note previste dallo standard MIDI (dalla 21 alla 108).


   120      121      122      123      124      125      126      127                                          9
   108      109      110      111      112      113      114      115      116      117      118      119      8
    96       97       98       99      100      101      102      103      104      105      106      107      7
    84       85       86       87       88       89       90       91       92       93       94       95      6
    72       73       74       75       76       77       78       79       80       81       82       83      5
   *60*      61       62       63       64       65       66       67       68       69       70       71      4
    48       49       50       51       52       53       54       55       56       57       58       59      3
    36       37       38       39       40       41       42       43       44       45       46       47      2
    24       25       26       27       28       29       30       31       32       33       34       35      1
    12       13       14       15       16       17       18       19       20       21       22       23      0 
     0        1        2        3        4        5        6        7        8        9       10       11     -1
|        |////////|        |////////|        |        |////////|        |////////|        |////////|        | 
|        |////////|        |////////|        |        |////////|        |////////|        |////////|        | 
|        |////////|        |////////|        |        |////////|        |////////|        |////////|        | 
|   C    |// C# //|   D    |// D# //|   E    |   F    |// F# //|   G    |// G# //|   A    |// A# //|   B    | 
|        |// D- //|        |// E- //|        |        |// G- //|        |// A- //|        |// B- //|        | 
|        |////////|        |////////|        |        |////////|        |////////|        |////////|        | 
|         '------'          '------'         |         '------'          '------'          '------'         | 
|             |                 |            |             |                 |                 |            | 
|             |                 |            |             |                 |                 |            | 
|             |                 |            |             |                 |                 |            | 
|     DO      |        RE       |     MI     |      FA     |       SOL       |       LA        |     SI     | 
|             |                 |            |             |                 |                 |            | 
|             |                 |            |             |                 |                 |            | 
 '-----------' '---------------' '----------' '-----------' '---------------' '---------------' '----------'      

Prendendo come riferimento una nota di frequenza conosciuta, tipicamente il la a 440 Hz, dal codice MIDI è possibile calcolare la frequenza di qualsiasi nota con la seguente formula:

frequenza = 440 * (12√2)(midicode-69)

Formula Python equivalente:

frequenza = 440 * (2**(1/12.0))**(midicode-69)

È quindi elementare modificare la classe Beep per accettare direttamente i codici MIDI anziché le frequenze.


Curiosità storica: l'home computer Sinclair ZX-Spectrum nel 1982 usava già una specie di codice midi come parametro per l'istruzione BEEP, solo traslato di 60 unità, in modo da far coincidere il do centrale con lo 0. In pratica scrivendo BEEP 1,3 si suonava per un secondo il re diesis dell'ottava 4, mentre con BEEP 1,-12 si suonava il do dell'ottava 3.



Un player note più evoluto

Il programma seguente implementa una classe Player che accetta di essere "caricata" tramite stringhe in pseudo linguaggio musicale, per poi riprodurle con il metodo play.

Il calcolo dei dati audio viene effettuato al momento del caricamento, pertanto in seguito è possibile riavviare più volte il motivo con il metodo play. Se si vuole suonare un altro motivo bisogna prima richiamare il metodo clear e procedere ad un nuovo caricamento.

#------------------------------------------------------------------------------
# pygame note player - by C.Fin 2014 - Codice Python2
#------------------------------------------------------------------------------
from math import sin, pi
import struct
import io
import wave
import pygame
import time

#------------------------------------------------------------------------------

class Player:

    def __init__(self):
        self.FRAMERATE = 44100
        self.SONG_END = pygame.USEREVENT + 1
        self.channel = pygame.mixer.Channel(1)
        self.channel.set_endevent(self.SONG_END)
        self.clear()
        self.notes = {    # codici midi ottava 3
            'C':  48, 
            'C#': 49,
            'C+': 49,
            'D-': 49,
            'D':  50,
            'D#': 51,
            'D+': 51,
            'E-': 51,
            'E':  52,
            'F':  53,
            'F#': 54,
            'F+': 54,
            'G-': 54,
            'G':  55,
            'G#': 56,
            'G+': 56,
            'A-': 56,
            'A':  57,
            'A#': 58,
            'A+': 58,
            'B-': 58,
            'B':  59
            }

    def clear(self):
        self.data = []
        self.data = []
        self.octave = 5
        self.bpm = 120.0
        self.t = 240/self.bpm
        self.structed = False


    def _encode(self, midicode):
        t_note = 7.0 / 8.0 * self.t
        t_pause = 1.0 / 8.0 * self.t
        alphastep = 2 * pi / self.FRAMERATE  
        f = 440 * (2**(1/12.0))**(midicode-69) if midicode > 0 else 0
        samples = int(t_note * self.FRAMERATE)
        for sample in range(samples):  
            alpha = alphastep * sample      # angolo per 1Hz
            y = 8000 * sin(alpha * f)  
            self.data.append(int(round(y)))  
        samples = int(t_pause * self.FRAMERATE)
        self.data.extend([0] * samples)


    def add(self, elements):
        '''
        Interpreta lo pseudolinguaggio musicale e calcola i dati audio RAW.
        Tutti gli elementi devono essere separati da almeno uno spazio.
        T190            190 note da 1/4 in un minuto
        L1              Imposta tempo note seguenti, nota intera
        L4              Imposta tempo note seguenti, nota 1/4  (60/T secondi)
        L8              Imposta tempo note seguenti, nota 1/8
        P4              Pausa 1/4
        C D E F A G B   note do re mi fa sol la si
        #               diesis (segue la nota relativa)
        +               diesis (segue la nota relativa)
        -               bemolle (segue la nota relativa)
        .               allunga durata della nota del 50% (segue la nota relativa)
        O5              Imposta l'ottava 5
        <               Scende di un'ottava con le note seguenti
        >               Sale di un'ottava con le note seguenti
        N60             Suona la nota midi 60  (la 0 crea solo una pausa)
        '''
        for element in elements.split():
            if element == '<': self.octave -= 1
            
            elif element == '>': self.octave += 1
            
            elif element.startswith('O'): self.octave = int(element[1:])
            
            elif element.startswith('T'): self.bpm = float(element[1:])
            
            elif element.startswith('L'): self.t = 240/self.bpm/int(element[1:])
            
            elif element.startswith('P'):
                bak_t = self.t
                self.t = 240/self.bpm/int(element[1:])
                self._encode(0)
                self.t = bak_t
                
            elif element.startswith('N'): 
                midicode = int(element[1:])
                self._encode(midicode)
                
            else:
                if element.endswith('.'):
                    midicode = self.notes[element[:-1]] + 12 * (self.octave - 3)
                    bak_t = self.t
                    self.t = 3.0 / 2.0 * self.t
                else:
                    midicode = self.notes[element] + 12 * (self.octave - 3)
                    bak_t = self.t
                self._encode(midicode)
                self.t = bak_t
                

    def play(self):
        if not self.structed:  # esegue struct.pack solo una volta
            self.data = struct.pack('h'*len(self.data), *self.data)
            self.structed = True
        self.myBuffer = io.BytesIO()
        my_wave = wave.open(self.myBuffer, 'wb')  
        my_wave.setnchannels(1)  
        my_wave.setsampwidth(2)  
        my_wave.setframerate(self.FRAMERATE)  
        my_wave.setnframes(0)  
        my_wave.setcomptype('NONE', 'NONE')  
        my_wave.writeframes(self.data)  
        my_wave.close()
        self.myBuffer.seek(0)
        self.channel.play(pygame.mixer.Sound(self.myBuffer))
        running = True
        while running:
            time.sleep(0.005)
            for event in pygame.event.get():
                if event.type == self.SONG_END: 
                    running = False

#------------------------------------------------------------------------------

ritornello1 = ('''
    T190 O5
    L8 G E A E G E
    L4 > C L8 C < E L4 G    
    L8 G E A E G E
    L4 G L8 G F# L4 F   
    L4 B B A
    L8 A G# L2 G     
    L8 G F# L2 F      
    L8 F E L2 C       
    L8 G E A E G E      
    L4 > C L8 C < E L4 G
    L8 G E A E G E     
    L4 G L8 G F# L4 F   
    L4 B B A         
    L8 A G# L2 G      
    L8 B A E G F D
    L4 C C P4
    ''')

ritornello2 = ('''
    L4 E > C < B
    L8 A G# A E A G
    F E F G A F
    L2 E.   
    L4 E F E
    L8 G# E B G L4 > D
    < E F E 
    L8 A E > C < A L4 > E 
    < E > C < B
    L8 A G# A E A G 
    F E F G A F
    L2 E.
    L4 E F E 
    L8 G# E B G L4 > D
    < L8 F E D# E > C < B
    L4 A A P4
    ''')

ritornello3 = ('''
    L4 F. L8 E D C
    L2 F L4 A
    > L2 C L4 D
    < L2 G.
    L4 B-.  L8 A G F
    L4 E. L8 F D E
    L4 C. L8 G A B- 
    L2 B L4 > C 
    < F. L8 E D C
    L2 F L4 A
    > L2 E- L4 D
    < L2 B- L4 G 
    L8 G F# G A B- > D
    L4 C. < L8 F G F
    L4 E. L8 > C < B- G
    L4 F F P4
    ''')

pygame.init()
player = Player()

player.add(ritornello1)
player.add(ritornello1)
player.add(ritornello2)
player.add(ritornello1)
player.add(ritornello3)

player.play()
pygame.quit()


Bibliografia