Track Deconstruction è una nuova rubrica che, attraverso l’analisi di brani più o meno famosi, ha l’obiettivo di spiegare come vengono scritte canzoni di successo e di farci capire che, per nostra fortuna, non ci sono particolari trucchi o segreti. Oggi analizziamo insieme il brano No Roots di Alice Merton.

Premessa

Se ascoltate la radio è molto probabile che conosciate già questa canzone: negli ultimi due mesi è andata in rotazione nelle principali stazioni italiane. Il motivo per cui l’ho scelta è molto semplice: fin dalla prima volta che l’ho sentito l’ho trovato orecchiabile e interessante da analizzare per la sua apparente semplicità.

Facendo una veloce ricerca si scopre che Alice Merton, classe 1993, è una cantante tedesco-canadese che, con Paul Grauwinkel, ha fondato un’etichetta discografica, la Paper Plane Records international con la quale ha pubblicato il suo singolo No Roots e l’omonimo EP.

Nessun talent show né major discografica o, addirittura, etichetta indipendente di successo alle spalle per supportare il suo singolo; con questo brano è riuscita ad arrivare al successo e le 79 milioni di visualizzazioni del video su YouTube – nel momento in cui scrivo – ne sono la prova.

Metro, BPM e struttura del brano

Quando analizzo un brano mi piace partire subito dalle cose più semplici: il metro è un classico 4/4, la velocità del brano è 116 bpm. Di norma preferisco non concentrarmi subito su note, accordi e arrangiamento perché richiedono un minimo di competenze musicali. Al contrario scrivere la struttura di un brano, ovvero al suddivisione in verso, ritornello, ecc… è una cosa piuttosto semplice che chiunque sappia contare fino a 10 e sia armato di carta e penna può fare senza troppa difficoltà.

Ho importato il brano in Ableton Live 10, impostato il metro e il BPM e mi sono assicurato che l’allineamento del punto di inizio coincidesse con 1.1.1.

Alice Merton - No Roots - Song Sctructure

La struttura del brano è la seguente:

Intro (6 bars)
|      |      |      |       |
|      |      |
Verse I (8)
|      |      |      |       |
|      |      |      |       |
Verse II (8)
|      |      |      |       |
|      |      |      |       |
Pre-Chorus (4)
|      |      |      |       |
Chorus (16)
|      |      |      |       |
|      |      |      |       |
|      |      |      |       |
|      |      |      |       |
Verse I (8)
|      |      |      |       |
|      |      |      |       |
Verse II (8)
|      |      |      |       |
|      |      |      |       |
Pre-Chorus (4)
|      |      |      |       |
Chorus (16)
|      |      |      |       |
|      |      |      |       |
|      |      |      |       |
|      |      |      |       |
Bridge (9)
|      |      |      |       |
|      |      |      |       |
|      |
Solo (8)
|      |      |      |       |
|      |      |      |       |
Chorus (16)
|      |      |      |       |
|      |      |      |       |
|      |      |      |       |
|      |      |      |       |

Noterete subito che, pur non trattandosi della classica struttura dei brani pop, non si discosta troppo da questo modello. Interessante notare l’intro lungo 6 battute (molto spesso,viene tagliato completamente dalla radio) e il bridge di 9 se consideriamo 8 battute effettive più una che serve per il lancio dell’assolo di sintetizzatore.

Riff principale

Tutto il brano è giocato su un riff suonato, all’inizio della canzone, dal basso. Il riff parte dalla nota B e si sviluppa come segue:

No Roots - Bass Intro
Il riff suonato dal basso nell’intro.

 

Siccome, come dicevamo prima, l’intro è stranamente lungo sei battute e non otto come accade di solito, ecco che nel verso è necessario allungare di due battute il riff. Nell’immagine sottostante ho utilizzato il pulsante Fold per mostrare solo le note suonate ed eliminare dalla vista le altre.

Bass (Verse)

 

Questo riff è così importante che si ripeterà in modo pressoché identico per tutta la durata del brano, chorus compreso. Ad un ascolto attento ci si rende conto, però, che c’è una piccola variazione nel secondo verso:

Armonia

Trattandosi di un brano pop, l’armonia è dell’intera canzone è piuttosto semplice: la tonalità è Bm e l’ho dedotta partendo dalle note suonate dal basso. Considerando le otto battute le verso, nelle prime quattro l’accordo è sempre Bm, nella quinta passa a Em per poi salire a G e A, entrambi maggiori, nella sesta battuta e ricadere su Bm nelle ultime due.

La progressione è, dunque:

I - IV - VI - VII

Inseriamo gli accordi nella struttura del verso:

Verse
|Bm    |%     |%     |%      |
|Em    |G  A  |Bm    |%      |
No Roots - Chords
Accordi fatti con il pianoforte (in blu) e riff di basso (giallo)

 

 

L’unica variazione a livello armonico, se così la possiamo definire, avviene nel pre-chorus dove vengono suonati gli accordi di EmGA per poi lanciare il ritornello che presenta la stessa progressione armonica del verso.

Ritmica

La parte ritmica di batteria è davvero semplice: cassa praticamente sempre sui quarti, il rullante, che entra nel ritornello e suona sul secondo e sul quarto quarto produce la classica ritmica chiamata backbeat. Le percussioni (shaker) sono in levare.

 

Da segnalare l’utilizzo di percussioni, in particolare floor tom per sottolineare i passaggi da una sezione all’altra del brano.

Melodia

Trascrivere la melodia di un brano è sempre la parte che trovo più difficile. Per mia fortuna in questo caso è composta da alcune cellule che si ripetono sempre uguali:

Alice Merton - Melody
La melodia del primo verso

Lascio a voi il divertimento di trascrivere la melodia del pre-chorus e del chorus.

Conclusione

Mancano ancora alcuni dettagli del brano ma il grosso del lavoro è stato fatto. Ora si tratta solo di rimettere insieme le idee, ordinarle e farle suonare insieme. Ecco il risultato finale che ho ottenuto dall’intro fino alla conclusione del primo ritornello:


Una volta capito come leggere con Processing i dati contenuti in un file CSV, il passaggio successivo è rendere questi dati leggibili e modificabili facilmente all’interno del programma convertendoli in variabili e array.

Ripartiamo dal nostro esempio precedente utilizzando sempre lo stesso data set:

/*
 * Leggere file CSV
 * Federico Pepe, 25.03.2018
 * http://blog.federicopepe.com/processing
 */

Table csv;

void setup() {
  csv = loadTable("data.csv", "header");

  println("Numero righe: " + csv.getRowCount());
  println("Numero colonne: " + csv.getColumnCount());
  
  for(int i = 0; i < csv.getRowCount(); i++) {
    println(csv.getFloat(i, 2));
  }
  
  noLoop();
}

void draw() {
}

Prima di procedere, assicuriamoci che il programma funzioni cliccando su run: nella console dovrebbero comparire i dati contenuti nella colonna relativa al 2009.

Utilizzare i metodi corretti per leggere i dati

Un aspetto importante da tenere presente è di utilizzare i metodi corretti per accedere ai dati. Spulciando nel reference di Table noterete che esistono diverse funzioni come .getFloat().getInt().getString().

Se sostituiamo nell’esempio precedente la riga println(csv.getFloat(i, 2)); con println(csv.getString(i, 2)); Processing non genererà nessun errore e continuerà a far girare il nostro programma ma, ora, quei valori sono considerati stringhe (quindi testo) e non più numeri.

Tutti questi metodi accettano due parametri: il primo indica la riga della tabella, il secondo la colonna. Per quest’ultimo possiamo usare sia un numero, partendo, come sempre a contare da 0, oppure una stringa contenente il nome della colonna.

Il codice, può essere sostituito con: println(csv.getFloat(i, "2009"));

Se sostituite sempre la stessa riga con println(csv.getFloat(i, "Descrizione")); la console vi restituirà tutti valori NaN ovvero Not a Number. Come dicevo, il programma continuerà a funzionare ma non in modo corretto.

Dal tabella CSV all’array

Passare tutti i dati in un array può essere molto comodo per utilizzare alcune funzioni specifiche di calcolo, come, ad esempio, min()max() che restituiscono, rispettivamente, il valore minimo e massimo di un array.

Anche se in questo momento siamo ancora lontani dall’idea di creare una visualizzazione di dati, dovremmo comunque cominciare a pensare a come utilizzeremo questi numeri.

Per come è stato strutturato il file CSV, ciascuna colonna rappresenta un anno con valori di vario tipo: temperatura minima, temperatura massima, eccetera; ma se noi volessimo rappresentare la variazione di uno stesso valore nel tempo dovremmo lavorare orizzontalmente e non verticalmente.

Sfruttiamo questo esempio per capire come passare i dati dal CSV a un array:

Creiamo un array di tipo float chiamato tempMin nel quale inseriremo tutti i valori di temperatura minima e modifichiamo il nostro ciclo for per girare non più sul numero di righe ma su quello delle colonne. Impostiamo l’inizio del ciclo for a 1 per saltare la prima colonna.

/*
 * Da una tabella CSV agli array
 * Federico Pepe, 01.04.2018
 * http://blog.federicopepe.com/processing
 */

Table csv;

float tempMin[];

void setup() {
  csv = loadTable("data.csv", "header");

  println("Numero righe: " + csv.getRowCount());
  println("Numero colonne: " + csv.getColumnCount());
  
  tempMin = new float[csv.getColumnCount()];
    
  for(int i = 1; i < csv.getColumnCount(); i++) {
    tempMin[i] = csv.getFloat(0, i);
  }
  
  printArray(tempMin);
  
  noLoop();
}

void draw() {
}

La dimensione dell’array è uguale al numero di colonne all’interno del file tempMin = new float[csv.getColumnCount()]; e inseriamo all’interno dell’array i valori float provenienti dalla riga 0, perché stiamo ignorando l’header, e di ciascuna colonna: tempMin[i] = csv.getFloat(0, i);

Dal risultato in console notiamo subito un problema: il primo valore dell’array è 0.0 perché, effettivamente, l’array contiene un valore in più, quello della colonna Descrizione.

Abbiamo due possibilità per risolvere il problema:

Modificare la grandezza dell’array sottraendo 1: tempMin = new float[csv.getColumnCount()-1]; e modificando l’inserimento dei valori nell’array sempre spostando l’indice indietro di 1 tempMin[i-1] = csv.getFloat(0, i);.

Questa soluzione funziona ma non è molto elegante, meglio cambiare il codice come segue: inizializziamo l’array con grandezza pari a 0: tempMin = new float[0]; e poi utilizziamo la funzione append() che espande l’array di un elemento e aggiunge il dato nella nuova posizione tempMin = append(tempMin, csv.getFloat(0, i));

Il codice completo

/*
 * Da una tabella CSV agli array
 * Federico Pepe, 01.04.2018
 * http://blog.federicopepe.com/processing
 */

Table csv;

float tempMin[];

void setup() {
  csv = loadTable("data.csv", "header");

  println("Numero righe: " + csv.getRowCount());
  println("Numero colonne: " + csv.getColumnCount());
  
  tempMin = new float[0];
    
  for(int i = 1; i < csv.getColumnCount(); i++) {
    tempMin = append(tempMin, csv.getFloat(0, i));
  }
  
  printArray(tempMin);
  
  noLoop();
}

void draw() {
}

Ora l’array è corretto e contiene esattamente tutti i valori previsti. Come dicevo, ora possiamo sfruttare l’array per ottenere il valore minimo e quello massimo molto semplicemente:

println("Il valore minimo è: " + min(tempMin));
println("Il valore massimo è: " + max(tempMin));

Cominciamo il nostro percorso per imparare a lavorare con i dati: in questo post vedremo insieme come utilizzare i file di tipo CSV in Processing.

Per chi non conoscesse questo tipo di file o ci li avesse mai usati si tratta, in breve, di file di testo in cui i valori sono separati da virgole. CSV, infatti, sta per comma separated values. Nella maggior parte dei casi questi file vengono esportati da Microsoft Excel, uno dei programmi più diffusi (e odiati) per gestire tabelle di dati.

Per dare la possibilità a chiunque mi segua sul blog di seguire gli esercizi, utilizzerò Google Sheet, alternativa gratuita e accessibile via browser del blasonato programma di Microsoft.

Ecco, quindi, il nostro primo set di dati: le statistiche metoclimatiche degli ultimi 10 anni (2008-2017) della regione Veneto, prese dal sito del Mipaaf e portate su Google Sheet.

Scarica i dati

Per esportare il file come CSV cliccate su File > Scarica Come > Valori separati da virgola (.csv, foglio corrente). Di seguito le immagini di come si presenta il file prima e dopo l’esportazione:

Dati in Processing: File CSV in Google Sheet
I dati visti in Google Sheet
Dati in Processing: File CSV in Atom
Gli stessi dati, esportati in CSV

Dati in un file CSV: semplice testo

Come dicevamo all’inizio, il file CSV esportato non è altro che un file di testo contenente dei valori. Per cominciare a leggerne il contenuto è sufficiente utilizzare la funzione loadStrings(): tale funzione accetta in input un file di testo e restituisce un array di stringhe.

Prima di procedere, consiglio di rinominare il file scaricato in data.csv. Non dimenticate di trascinarlo all’interno della finestra di Processing per aggiungerlo al nostro sketch.


/*
 * Leggere file CSV
 * Federico Pepe, 25.03.2018
 * http://blog.federicopepe.com/processing
 */

String[] csv;

void setup() {
  csv = loadStrings("data.csv");
  printArray(csv);
  noLoop();
}

void draw() {
}

Con queste poche righe di codice nella console ogni riga del file viene mostrata come un nuovo elemento dell’array di stringhe.

[0] "Descrizione,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017"
[1] "Temp. minima (°C),6.9,7.3,6.7,7.4,7.2,7.7,8.7,8,7.6,7.1"

Fino a qui niente di difficile ma a noi interessa accedere ai singoli valori presenti in ciascuna colonna. A questo punto ci torna utile riprendere gli array bidimensionali: un sistema che ci permette di rappresentare facilmente una struttura formata da righe e colonne, proprio come un file excel/csv.

Modifichiamo, dunque, il nostro codice come segue:

/*
 * Leggere file CSV
 * Federico Pepe, 25.03.2018
 * http://blog.federicopepe.com/processing
 */

String[] csv;
String[][] dati;

void setup() {
  csv = loadStrings("data.csv");
  dati = new String[csv.length][10]; 
  
  for(int i = 0; i < csv.length; i++) {
    dati[i] = csv[i].split(",");
    printArray(dati[i]);
  }
  
  noLoop();
}

void draw() {
}

Abbiamo aggiunto un array bidimensionale chiamato dati la cui dimensione è determinata dal numero di righe [csv.length] e dal numero di colonne meno uno perché si conta sempre da zero [10].

Con un semplice ciclo for accediamo a tutte le righe del file e, utilizzando la funzione .split(",") separiamo tutti i valori che sono separati dalla virgola.

Affinché sia tutto il più chiaro possibile possiamo fare un po’ di esperimenti con println inserendo nel primo valore dell’array il numero della riga e nel secondo quello della colonna.

println(dati[3][2]); restituisce il valore 0.7 che corrisponde, infatti, alla cella nella quarta riga “Scarto dal clima” e nella terza colonna “2009”.

Leggere file CSV in modo più semplice: Table

Siamo riusciti nel nostro intento ma credo sia ovvio che il metodo che abbiamo usato non sia il più congeniale.

Per nostra fortuna i creatori di Processing avevano già pensato a questa evenienza e hanno creato un oggetto specifico chiamato Table che, come è facile intuire dal nome, rappresenta già una tabella completa di righe e colonne.

Grazie ai numerosi metodi disponibili per gli oggetti di tipo Table è possibile lavorare con i dati in modo semplice e intuitivo.

Aggiorniamo il nostro codice:


/*
 * Leggere file CSV
 * Federico Pepe, 25.03.2018
 * http://blog.federicopepe.com/processing
 */

Table csv;

void setup() {
  csv = loadTable("data.csv", "header");

  println("Numero righe: " + csv.getRowCount());
  println("Numero colonne: " + csv.getColumnCount());
  
  for(int i = 0; i < csv.getRowCount(); i++) {
    println(csv.getFloat(i, 2));
  }
  
  noLoop();
}

void draw() {
}

Abbiamo sostituito i due array di stringhe con un oggetto di tipo Table chiamato csv. Attraverso la funzione loadTable() carichiamo i dati all’interno della variabile. Passando il parametro “header” stiamo dicendo a Processing di ignorare la prima riga del file che contiene l’intestazione.

Attraverso le funzioni .getRowCount() .getColumnCount() accediamo al numero di righe e colonne del file e, infine, con il nostro ciclo for stampiamo in console i valori di tipo float contenuti nella terza colonna (quindi quelli relativi al 2009).

Conclusione

In questo post abbiamo messo molta carne al fuoco e abbiamo cominciato ad addentrarci nel mondo dei dati e dei file CSV. Assicuratevi di aver compreso bene tutte le funzioni e gli esempi inseriti in questo post prima di proseguire con la lettura del prossimo.


Negli ultimi giorni ho ricevuto diverse richieste di aiuto per problemi di codice in Processing; ecco la soluzione a uno dei problemi che mi sono stati posti.

Ho deciso di chiamare questo Coding Rescuecambiare la tonalità di alcuni pixel.

Il problema

Il quesito era piuttosto articolato:

  • Il programma deve caricare un’immagine dal disco.
  • Il programma decide se modificare o no il pixel in base ad un valore casuale.
  • Il processo di trasformazione viene controllato dal click  del mouse: un primo click avvia la trasformazione che, con un successivo click, viene messa in pausa. Un ulteriore click fa ripartire la trasformazione e così via.
  • I pixel devono essere modificati di continuo.
  • Il compito deve essere svolto mediante la funzione creaImmagine(), che accetta in ingresso un oggetto di tipo PImage e rende in uscita un oggetto di tipo PImage, che sarà l’immagine modificata come da specifiche, e la funzione calcolaPixel(), che accetta in ingresso un oggetto di tipo color e rende in uscita un oggetto di tipo color, che sarà il pixel modificato come da specifiche.
  • creaImmagine() crea una nuova immagine utilizzando su ciascun pixel la funzione calcolaPixel().
  • calcolaPixel() calcola il nuovo pixel nel seguente modo: siano r, g, b i livelli di canale di un pixel. Sia treshold un valore numerico posto inizialmente a 0.5. Sia r un valore casuale compreso tra 0 e 1, generato per ogni pixel. Se r>soglia, allora la funzione lascia il pixel inalterato, in caso contrario il programma calcola i nuovi valori rm, gm, bm (che definiscono il pixel
    modificato) nel seguente modo:

    • rm = 0.393∗r+0.769∗g+0.189∗b
    • gm = 0.349∗r+0.686∗g+0.168∗b
    • bm = 0.272∗r+0.534∗g+0.131∗b

    In ogni caso i valori finali di r, g, b devono essere valori leciti, cioè compresi tra 0 e 255.

  • Quando l’utente preme il tasto “+” la soglia viene incrementata di 0.1 mentre, con la pressione del tasto “-” la soglia viene decrementata di 0.1.

La soluzione


/*
 * Coding Rescue #3 - Cambiare la tonalità di alcuni pixel
 * Federico Pepe, 21.01.2018
 * http://blog.federicopepe.com/processing
 */

// Creo le variabili necessarie;
float threshold = 0.5;
boolean work = true;
PImage original, edited;

void setup() {
  // Nella funzione di setup carico il file dall'hard disk;
  size(1, 1);
  surface.setResizable(true);
  selectInput("Select a file to process:", "fileSelected");
}

void draw() {
  if (original != null) {
    edited = original.copy();
    /*
     * Se la variabile work, il cui valore dipende dal click del mouse è true, mostro
     * l'immagine originale, altrimenti avvio la modifica
     */
    if (work) {
      image(edited, 0, 0);
    } else {
      image(creaImmagine(edited), 0, 0);
    }
  }
}

// Funzione per gestire il caricamento dei file da HD
// https://processing.org/reference/selectInput_.html
void fileSelected(File selection) {
  if (selection == null) {
    println("Window was closed or the user hit cancel.");
  } else {
    println("User selected " + selection.getAbsolutePath());
    original = loadImage(selection.getAbsolutePath());
    surface.setSize(original.width, original.height);
  }
}

// Funzione creaImmagine che, come da specifiche, accetta in ingresso un oggetto PImage
// e restituisce un oggetto PImage;
PImage creaImmagine(PImage img) {
  // Carico i pixel in un array
  img.loadPixels();
  for (int y = 0; y < img.height; y++) {
    for (int x = 0; x < img.width; x++) {
      int loc = x + y * img.width;
      // Ottengo i valori R, G, B di ciascun pixel;
      float r = red(img.pixels[loc]);
      float g = green(img.pixels[loc]);
      float b = blue(img.pixels[loc]);
      // Lancio la funziona calcolaPixel
      img.pixels[loc] = calcolaPixel(color(r, g, b));
    }
  }
  img.updatePixels();
  return img;
}

color calcolaPixel(color c) {
  // Valore casuale, come da specifiche
  float r = random(0, 1);

  if (r > threshold) {
    // Se r è maggiore della soglia, restituisco il colore originale
    return c;
  } else {
    // Altrimenti calcolo il nuovo valore del pixel come richiesto
    // la funzione constrain mi assicura che il valore calcolato sia compreso tra 0 e 255;
    float rm = constrain(0.393*red(c)+0.769*green(c)+0.189*blue(c), 0, 255);
    float gm = constrain(0.349*red(c)+0.686*green(c)+0.168*blue(c), 0, 255);
    float bm = constrain(0.272*red(c)+0.534*green(c)+0.131*blue(c), 0, 255);
    // Restituisco i nuovi valori
    return color(rm, gm, bm);
  }
}

// Con la funzione keyPressed determino la pressione dei tasti + e -
void keyPressed() {
  if (key == '+') {
    threshold += 0.1;
  }
  if (key == '-') {
    threshold -= 0.1;
  }
  // Utilizzo la funzione round (di seguito) per arrotondare il valore a 2 decimali;
  round(threshold, 2);
}
// Alla pressione del mouse, cambio il valore di work da true a false e viceversa
void mouseClicked() {
  work = !work;
}
// Funzione utile per limitare il numero di decimali in un float
// https://stackoverflow.com/questions/9627182/how-do-i-limit-decimal-precision-in-processing
float round(float number, float decimal) {
  return (float)(round((number*pow(10, decimal))))/pow(10, decimal);
} 


Aggiornamento 17 gennaio 2017: Ableton 10 arriverà il 6 febbraio.

Si è cominciato a parlare di Ableton 10 da fine agosto quando, in un video di DJ Jazzy Jeff, qualcuno aveva notato alcune differenze rispetto alla versione 9.

Le speculazioni sono andate avanti per diverse settimane finché, il 2 novembre, Ableton ha annunciato ufficialmente che la nuova release del software era in lavorazione e che sarebbe stata disponibile a inizio 2018.

Arriviamo a oggi o, meglio, ad un paio di giorni fa: Ableton 10 è entrato nella fase di public beta per chi possiede una licenza standard suite di Live 9.

È un segnale chiaro che ci stiamo avvicinando alla release finale del programma e Ableton ha bisogno che gli utenti lo testino e scovino bug prima del rilascio ufficiale.

Dall’annuncio della beta pubblica ho letto parecchi commenti critici nei confronti dell’aggiornamento: molti ritengono che il prezzo sia sproporzionato rispetto alle novità introdotte. Io ho avuto la fortuna di essere selezionato tra i beta tester a inizio novembre e, superato lo scetticismo iniziale, posso dirvi che questo aggiornamento vale ogni centesimo.

In questo post troverete una panoramica sulle novità che, personalmente, ho trovato più interessanti.

Una nuova interfaccia

Interfaccia Live 10

Partiamo dalla cosa più ovvia: la grafica. Una nuova icona, un nuovo font – Ableton Sans – che rende più leggibile l’intera interfaccia del programma, e dei nuovi temi: Light, Mid Light, Mid Dark, DarkLive 9.

Su schermi ad alta risoluzione (HiDPI) risulta tutto molto leggibile e definito; ricordo ancora quanto ho benedetto l’aggiornamento di Live 9 che ha introdotto il supporto al mio retina display.

Capture: non perdere mai un’idea

Una delle novità più attese e che, dal mio punto di vista, giustifica l’investimento per l’aggiornamento è  la funzione Capture. Ableton sarà perennemente in “ascolto” di tutti gli input che arrivano sulle tracce MIDI. Alla pressione del tasto capture, inserito accanto al pulsante di registrazione della sessione, viene creata automaticamente una clip.

La cosa che più mi ha colpito di questa funzione quando l’ho provata la prima volta è che Ableton cerca di mantenere sia il tempo che il groove della nostra performance adattando, di conseguenza, le impostazioni del progetto.

Nuovi device

Wavetable - Ableton 10

Come ogni aggiornamento che si rispetti, sono stati introdotti dei nuovi dispositivi (di cui parleremo in modo più esteso prossimamente):

  • Wavetable: un nuovo sintetizzare a due oscillatori che, com’è facilmente intuibile dal nome, sfrutta i principi della la sintesi wavetable.
  • Echo: un nuovo effetto delay con molte possibilità di modulazione.
  • Drum Buss: un processore di dinamica progettate per le batterie dal carattere analogico.
  • Pedal: un distorsore/overdrive/fuzz che si ispira a tre classici pedali da chitarra.

Sono stati migliorati anche due device già presenti nelle versioni precedenti: Utility ed EQ Eight.

Arrangiamento, automazioni e navigazione

I programmatori di Ableton hanno concentrato i loro sforzi sul miglioramento della modalità arrangiamento: ora è più facile fare editing, arrangiare e creare automazioni.

Queste le novità principali e il relativo shortcut, se presente:

  • È possibile fare lo stretch delle clip audio prendendo Shift e trascinando il bordo.
  • È possibile muovere il contenuto interno di una clip premendo Ctrl+Shift (PC) / Alt+Shift (Mac) senza spostare la clip. Tenendo premuto Alt (PC) / Cmd (Mac) si disattiva temporaneamente la griglia.
  • Le note MIDI vengono riprodotte anche se il playhead è partito dopo l’effettivo inizio della nota (MIDI Note Chasing).
  • Il doppio click su una traccia MIDI crea una clip.
  • È possibile zoomare le tracce premendo Alt e verticalmente premendo Ctrl (PC) / Cmd (Mac).
  • Zoom in/out di una porzione di tempo selezionata (Z / Shift+Z).
  • Aggiunta funzione mostra tutte le tracce (S).
  • È possibile mostrare/nascondere tutte le automazioni (A).
  • Segmenti di automazione possono essere spostati orizzontalmente

Miglioramenti del workflow

In generale questo aggiornamento di Ableton 10 si caratterizza per una serie di piccoli e quasi invisibili miglioramenti che, però, cambieranno e velocizzeranno il vostro modo di lavorare:

  • Multi-Clip Editing: è possibile editare fino a otto clip midi nella Vista dettagliata. Se le clip sono di lunghezza differente, Ableton creerà automaticamente un loop per allineare tutte le clip.
  • Gruppi annidati: i gruppi possono essere inseriti in altri gruppi. Non c’è limite al numero di gruppi annidati gli uni negli altri.
  • Reset con doppio click: facendo doppio click su qualsiasi parametro, il suo valore viene resettato (finalmente!).
  • Raccolte nel browser: è stata aggiunta nel browser la sezione Raccolte che permette di categorizzare il contenuto della libreria per tag colori.
  • Metronomo: non solo sono stati aggiunti dei nuovi suoni tra cui scegliere ma è anche possibile impostare il metronomo per contare le suddivisioni ritmiche.
  • Backup Folder: ogni volta che salviamo il progetto, viene creata una copia di backup della versione precedente. In questo modo è possibile risalire fino a 10 salvataggi precedenti.
  • Pan Split-stereo: facendo Ctrl+Click sul knob del Pan è possibile impostarlo come Stereo Split.

Max 8 e Max for Live

Max for Live è ora integrato direttamente nel programma: non è necessario installare alcun pacchetto esterno. Nella versione beta di Ableton 10 è presente una versione beta di Max 8. Beta-inception.

Tra le novità: nuovi strumenti e il supporto multi input/output.

Conclusione

Quelle descritte in questo post sono solo una parte delle novità presenti nella versione beta attuale di Ableton Live 10. Per farvi un’idea, nel manuale ufficiale il capitolo What’s New in Live 10 occupa ben 10 pagine!

Ormai dovrebbero mancare poche settimane al rilascio della nuova versione; come già detto, io non vedo l’ora.


Nel mondo digitale tutto è un insieme di dati: che si tratti di un testo, una foto oppure un video, stiamo parlando di una sequenza di 1 e 0 che chiamiamo comunemente file.

Con questo post inauguro una nuovo capitolo su questo blog in cui scopriremo insieme come utilizzare Processing per lavorare con file e dati.

Quale potrebbe essere il vantaggio di utilizzare un linguaggio di programmazione per questo genere di attività? Perché non possiamo utilizzare strumenti per grafici o designer come Photoshop o Illustrator? La risposta è semplice: uno dei vantaggi dei computer rispetto alla mente umana è che sono in grado di processare velocemente numeri ed eseguire operazioni ripetitive e complesse molto velocemente.

La vista di un file di Excel pieno di righe e colonne può scatenare il panico nelle persone sane di mente ma per un software non servono altro che due cicli for per accedere al contenuto di ciascuna cella. Quello che impareremo a fare sarà, poi, assegnare quel contenuto a una (o più) variabili e il gioco è fatto.

Pensiamo alle visualizzazioni di dati che negli ultimi anni sono diventate onnipresenti su siti web, riviste e quotidiani. Il loro successo è dovuto al fatto che, grazie a un linguaggio di tipo visuale, rendono più accessibile il contenuto della storia, anche quando include una grande mole di dati. A differenza della carta stampata realizzando un’infografica con Processing si apre un ventaglio di possibilità: possiamo renderla interattiva, in 3D, può essere su uno schermo oppure proiettata su una parete.

Tipi di dato

Quali saranno i tipi di dati su cui lavoreremo? Abbiamo già avuto modo di parlare di immagini e pixel (tema che approfondiremo), ma in questa sezione ci concentreremo su:

  • File di testo (.txt)
  • Tabelle di dati di Excel (.csv, .tsv)
  • Pagine web (.html)
  • Feed (.xml)
  • API (.json)

Da qualche giorno ho aggiornato la grafica del sito che giunge, così, dopo il reboot nel 2013, alla sua terza o quarta versione. L’obiettivo, come sempre, è quello di rendere tutti i contenuti – presenti e futuri – più fruibili.

Ho cercato di curare nei minimi dettagli la leggibilità sia su dispositivi mobili che su schermi più grandi e ho prestato particolare attenzione al miglioramento dei tempi di caricamento pagina.

Sempre sull’onda del minimalismo assoluto, ho eliminato tutti i fronzoli non necessari: barre laterali,  header, immagini, ecc.

Tutto il codice pubblicato nei vari articoli che era hostato su GitHub è stato riportato sul sito, la pagina di Processing è stata completamente ristrutturata. Ho riordinato tutte le categorie del blog e riassegnato gli articoli alla giusta sotto-categoria.

Oltre alla forma si evolveranno anche i contenuti. In generale spero di riprendere un ritmo di pubblicazione più alto: nel 2017 mi ero imposto almeno un post al mese e mi piacerebbe arrivare, nel 2018, ad almeno uno a settimana.

Oltre alla parte relativa alla programmazione – che rimarrà e che si amplierà nel prossimo futuro – entreranno anche nuove categorie.

Proverò ad esplorare anche nuovi mezzi e, una volta per tutte, cercherò di curare quelli che ho già attivato [Facebook, Newsletter] e che non ho mai seguito nel modo giusto.

Non volevo fare il classico post di bilancio del 2017 e di buoni propositi per il 2018. Ho deciso di pubblicare questo post per fissare un punto di inizio e la strada che intendo seguire.

Buona lettura.


Secondo capitolo della rubrica Coding Rescue dove provo a risolvere i vostri problemi con Processing. Questa volta la consegna è piuttosto articolata, riassumo i punti salienti:

  • Lo scopo del programma è quello di “binarizzare” un’immagine ovvero fare in modo che, al click del mouse, i colori dell’immagine vengano modificati utilizzando la funzione binarize(). La funzione imposta un pixel bianco se la sua luminosità è maggiore di una soglia impostata e nero se inferiore.
  • per calcolare la luminosità, si deve utilizzare la funzione brightness().
  • impostare la soglia iniziale a un valore predefinito (a scelta) ma implementare una funzione calculateThreshold() con cui ricalcolarla dinamicamente utilizzando al distanza percorsa dal mouse tra due frame successivi.

Binarizza un’immagine, la soluzione

binarizza un'immagine con Processing

Ecco qui il codice:


/*
 * Coding Rescue #2 - Binarizzare un'immagine
 * Federico Pepe, 26.10.2017
 * http://blog.federicopepe.com/processing
 */
float threshold = 150;
PImage immagine, imgbin;
boolean bin = true;

void setup() {
  size(700, 542);
  immagine = loadImage("corgi-photo.jpg");
  image(immagine, 0, 0);
  frameRate(2);
} 

void draw() {
  threshold = calculateThreshold();
  if (bin) {
    imgbin = immagine.copy();
    image(binarize(imgbin), 0, 0);
  } else {
    image(immagine, 0, 0);
  }
}

void mouseClicked() {
  bin = !bin;
}  

PImage binarize(PImage img) {
  img.loadPixels();
  for (int y = 0; y < img.height; y++) {
    for (int x = 0; x < img.width; x++) { int loc = x + y * img.width; if (brightness(img.pixels[loc]) > threshold) {
        img.pixels[loc] = color(0);
      } else {
        img.pixels[loc] = color(255);
      }
    }
  }
  img.updatePixels();
  return img;
}

float calculateThreshold() {
  float d = dist(pmouseX, pmouseY, mouseX, mouseY);
  threshold = d;
  return threshold;
}

Breve introduzione alla Glitch Art

Per Glitch Art intendiamo la pratica sfruttare e/o introdurre in un’opera degli errori analogici o digitali per fini estetici. Nel mondo dell’analogico si interviene direttamente sugli apparecchi elettronici che registrano o riproducono l’opera. Per quanto riguarda il digitale, invece, tale manipolazione è resa ancora più semplice dagli strumenti tecnologici di cui disponiamo che ci consentono di accedere direttamente ai dati e di farne ciò che vogliamo. Questa pratica è anche chiamata databending.

Non si tratta di un movimento artistico nato, come si potrebbe pensare, negli ultimi anni con la democratizzazione della tecnologia e degli strumenti creativi ma, al contrario, affonda le sue radici nella prima metà del novecento: il filmato qui sotto A Colour Box dell’artista neo-zelandese Len Lye e datato 1935 è considerato uno dei primi esempi di glitch art.

Benché negli ultimi anni abbia riscosso un sempre maggiore successo, questo movimento artistico non è mai diventato mainstream. Possiamo sfruttare quello che abbiamo imparato a fare con Processing per fare i nostri esperimenti di glitch art e per studiare qualche nuova funzione del linguaggio.

Usiamo Processing per creare un effetto glitch

Lo scopo di oggi è prendere una foto e glitcharla. Nell’esempio proposto non andrò a introdurre un vero e proprio errore nell’immagine ma andrò a manipolare direttamente l’array di pixel per ricreare un effetto artistico simil-glitch. Il motivo è presto detto: per ottenere un effetto artistico godibile è meglio evitare di usare variabile non controllabili.

Glitch fotografia Processing
Immagine di partenza

L’immagine di partenza non è stata scelta in modo casuale: dal momento che useremo la funzione brightness(), che restituisce il valore della luminosità di un colore, la foto presenta un forte contrasto tra lo sfondo e le sagome nere e la parte centrale illuminata dalle fiamme.

/*
 * Glitch di un'immagine con Processing
 * Federico Pepe, 23.09.2017
 * http://blog.federicopepe.com/processing
 */

PImage img;
int minThreshold = 200;
int maxThreshold = 255;

void setup() {
  size(1, 1);
  surface.setResizable(true);
  img = loadImage("fire.jpg");
  surface.setSize(img.width, img.height);  

  noStroke();
  noLoop();
}

void draw() {
  background(255);
  image(img, 0, 0);
  img.loadPixels();

  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {

      if (brightness(img.pixels[y*width+x]) > minThreshold && brightness(img.pixels[y*width+x]) < maxThreshold) {
        color c = get(x, y);
        stroke(c);
        line(x, y, x, y+2);
        noFill();
      }
    }
  }
}

void mousePressed() {
  save("export/export.png");
}

Rispetto al codice visto negli ultimi post, ho aggiunto due variabili di soglia minTresholdmaxThreshold e, all'interno del classico ciclo for che va ad analizzare ogni singolo pixel dell'immagine, introdotto un semplice controllo condizionale che, qualora il risultato sua true disegna una linea verticale di 2 pixel dello stesso colore del pixel analizzato.

Glitch immagine esportata

Il risultato è sicuramente particolare e d'effetto. Potete provare a giocare un po' con i parametri di soglia oppure sostituendo il disegno della linea con un quadrato. Un'altra possibilità potrebbe essere quella di non utilizzare il parametro della luminosità ma andare a scegliere la saturazione o il colore stesso.

Glitch with squares
Lo stesso identico codice di prima ma, in questo esempio, ho sostituito line() con rect()

Nelle ultime settimane mi è capitato di fare diversi lavori di grafica – che non è propriamente il campo in cui sono più ferrato – e mi sono sempre trovato in crisi nella scelta della palette di colori più adatta a quello che stavo facendo.

Mentre ero alla ricerca della giusta combinazioni di colori su siti come Coolors e Adobe Color mi è tornata in mente una frase di Joshua Davis detta in uno dei suoi video (non cito testualmente, vado a memoria): le palette di colori che funzionano meglio sono quelle legate a immagini a cui siamo abituati, come un tramonto sul mare oppure le gradazioni di verde in un bosco in montagna. Ecco perché estrarre i colori da una foto è uno dei modi migliori per trovare delle combinazioni che funzionano.

Da qui l’illuminazione: perché non creare uno sketch in Processing per aiutarmi?

Leggere i colori da un’immagine

Partiamo riciclando il codice visto nell’ultimo post: Modificare la dimensione della finestra in base all’immagine caricata. Creiamo un oggetto di tipo PImage e facciamo in mondo che la finestra si ridimensioni automaticamente al caricamento del file. Nel codice sotto riportato noterete un +50 pixel nell’altezza che servirà come spazio per rappresentare i colori della palette in tempo reale.

Per questo sketch ho deciso di utilizzare l’immagine di un prato fiorito:
Leggere i colori da un'immagine con Processing

Ricordatevi di inserire l’immagine nella cartella /data come spiegato qui.

/*
 * Una palette di colori da un'immagine
 * Federico Pepe, 28.08.2017
 * http://blog.federicopepe.com/processing
*/

PImage img;

void setup() {
  size(1, 1);
  surface.setResizable(true);
  img = loadImage("flowers.jpg");
  surface.setSize(img.width, img.height+50); 
}

void draw() {
}

Creiamo due variabili di tipo integer: la prima, numPoints indica il numero di colori di cui sarà composta la nostra palette. Ho deciso di chiamarla points perché prenderemo i dati colori da dei punti nell’immagine. La seconda pointX la utilizzeremo come riferimento per calcolare il valore di x dei punti scelti che, ai fini di questo esempio, saranno equidistanti.

Aggiungiamo un po’ di codice nel ciclo draw(): mostriamo l’immagine come sfondo e carichiamo l’array di pixel. Come anticipato, assegniamo a pointX il valore della larghezza della foto caricata diviso il numero di punti.

Utilizziamo il classico ciclo for per trovare i colori dei punti. Creo un minimo di interfaccia per aiutarmi nel lavoro: intorno ai punti di riferimento disegno un cerchio.

/*
 * Una palette di colori da un'immagine
 * Federico Pepe, 28.08.2017
 * http://blog.federicopepe.com/processing
*/

PImage img;
// Numero di "punti" della nostra palette
int numPoints = 5;
// Valore x di riferimento
int pointX;

void setup() {
  size(1, 1);
  surface.setResizable(true);
  img = loadImage("flowers.jpg");
  surface.setSize(img.width, img.height+50); 
}

void draw() {
  image(img, 0, 0);
  img.loadPixels();
  pointX = img.width / numPoints;
  
  for(int i = 0; i <= numPoints-1; i++) {
    int x = pointX*i;
    int y = mouseY;
    
    if(mouseY <= 0 || mouseY >= img.height) {
      y = img.height/2;
    }
    stroke(255);
    noFill();
    ellipse(x, y, 25, 25);
    color c = img.pixels[x + y * img.width];
    println(c);
  }
  
}

Avendo deciso di lasciare il valore di y variabile e dipendente dalla posizione y del mouse, ho deciso di inserire un controllo condizionale per verificare di non uscire dall’immagine stessa causando un errore Array out of bounds sull’array di pixel.

Nella console vengono mostrati i valori di c ma non sono utilizzabili in alcun programma di grafica perché non sono i classici valori HEX, RGB o HSB.

Mostriamo la palette di colori

Sfrutto i 50 pixel di margine di abbondanza rispetto alla foto per mostrare in tempo reale i colori dei pixel di riferimento. Farlo è molto semplice: creo dei rettangoli con, come fill, il colore estratto in precedenza. Sfrutto questo passaggio per centrare i punti di analisi rispetto alla larghezza della finestra e ai rettangoli in basso.

Palette di colori da immagine

/*
 * Una palette di colori da un'immagine
 * Federico Pepe, 28.08.2017
 * http://blog.federicopepe.com/processing
 */

PImage img;
// Numero di "punti" della nostra palette
int numPoints = 5;
// Valore x di riferimento
int pointX;

void setup() {
  size(1, 1);
  surface.setResizable(true);
  img = loadImage("flowers.jpg");
  surface.setSize(img.width, img.height+50);
}

void draw() {
  image(img, 0, 0);
  img.loadPixels();
  pointX = img.width / numPoints;

  for (int i = 0; i <= numPoints-1; i++) {
    int x = pointX/2+pointX*i;
    int y = mouseY;

    if (mouseY <= 0 || mouseY >= img.height) {
      y = img.height/2;
    }

    stroke(255);
    noFill();
    ellipse(x, y, 25, 25);

    color c = img.pixels[x + y * img.width];

    fill(c);
    rect(pointX*i, img.height, pointX, 50);
  }
}

Otteniamo i valori HEX dei colori

A questo punto il programma funziona nel modo corretto, non ci resta che fare dei piccoli miglioramenti all’interfaccia: grazie alla funzione hex(value, digits) possiamo ottenere il valore esadecimale del colore. Come indicato nel reference, il numero viene rappresentato con un massimo di 8 caratteri ma possiamo ottenerne 6, come siamo abituati, aggiungendo indicando il secondo parametro alla funzione.

Palette colori GIF

Ecco qui il codice:

/*
 * Una palette di colori da un'immagine
 * Federico Pepe, 28.08.2017
 * http://blog.federicopepe.com/processing
 */

PImage img;
// Numero di "punti" della nostra palette
int numPoints = 5;
// Valore x di riferimento
int pointX;

void setup() {
  size(1, 1);
  surface.setResizable(true);
  img = loadImage("flowers.jpg");
  surface.setSize(img.width, img.height+50);
}

void draw() {
  image(img, 0, 0);
  img.loadPixels();
  pointX = img.width / numPoints;

  for (int i = 0; i <= numPoints-1; i++) {
    int x = pointX/2+pointX*i;
    int y = mouseY;

    if (mouseY <= 0 || mouseY >= img.height) {
      y = img.height/2;
    }

    stroke(255);
    noFill();
    ellipse(x, y, 25, 25);

    color c = img.pixels[x + y * img.width];

    fill(c);
    rect(pointX*i, img.height, pointX, 50);
    
    fill(255);
    textAlign(CENTER);
    text("#"+hex(c,6), x, img.height+30);
    
  }
}

Salvare la palette di colori in un file di testo

Siamo quasi alla fine del nostro lavoro: l’unico passaggio che ci manca è salvare la palette di colori in un file .txt in modo da poterli usare a nostro piacimento anche in programmi esterni.

Aggiungiamo qualche riga di codice: creiamo una nuova variabile di tipo StringList il cui contenuto verrà inizializzato a ogni ciclo di draw con la funzione .clear();

Alla pressione del tasto del mouse, verrà salvato il file chiamato palette.txt. La conversione della StringList in array è necessaria perché la funzione saveStrings si aspetta come secondo parametro un array e non una lista.

/*
 * Una palette di colori da un'immagine
 * Federico Pepe, 28.08.2017
 * http://blog.federicopepe.com/processing
 */

PImage img;
// Numero di "punti" della nostra palette
int numPoints = 5;
// Valore x di riferimento
int pointX;
// Salviamo i valori HEX in una lista di stringhe
StringList palette = new StringList();

void setup() {
  size(1, 1);
  surface.setResizable(true);
  img = loadImage("flowers.jpg");
  surface.setSize(img.width, img.height+50);
}

void draw() {
  image(img, 0, 0);
  
  img.loadPixels();
  pointX = img.width / numPoints;
  
  palette.clear();
  
  for (int i = 0; i <= numPoints-1; i++) {
    int x = pointX/2+pointX*i;
    int y = mouseY;

    if (mouseY <= 0 || mouseY >= img.height) {
      y = img.height/2;
    }

    stroke(255);
    noFill();
    ellipse(x, y, 25, 25);

    color c = img.pixels[x + y * img.width];

    fill(c);
    rect(pointX*i, img.height, pointX, 50);
    
    fill(255);
    textAlign(CENTER);
    text("#"+hex(c,6), x, img.height+30);
    // Aggiungiamo il colore come stringa all'array
    palette.append("#"+hex(c,6));
    
  }
}

void mousePressed() {
  saveStrings("palette.txt", palette.array());
}