Rilevamento della presenza iniziale

Sto sviluppando un sistema come aiuto per i musicisti che eseguono la trascrizione. L’objective è quello di eseguire la trascrizione automatica della musica (non deve essere perfetta, in quanto l’utente correggerà glitch / errori successivi) su una registrazione monofonica a strumento singolo. Qualcuno qui ha esperienza nella trascrizione automatica della musica? O l’elaborazione del segnale digitale in generale? L’aiuto di chiunque è molto apprezzato, indipendentemente dal tuo background.

Finora ho studiato l’uso della Trasformata di Fourier veloce per il rilevamento del passo, e un numero di test sia in MATLAB che nei miei programmi di test Java hanno dimostrato che è abbastanza veloce e preciso per le mie esigenze. Un altro elemento del compito che dovrà essere affrontato è la visualizzazione dei dati MIDI prodotti in forma di spartito, ma questo è qualcosa a cui non sono interessato in questo momento.

In breve, quello che sto cercando è un buon metodo per il rilevamento delle note iniziali, ovvero la posizione nel segnale in cui inizia una nuova nota. Poiché gli insiemi lenti possono essere abbastanza difficili da rilevare correttamente, inizialmente utilizzerò il sistema con le registrazioni di pianoforte. Ciò è dovuto in parte anche al fatto che suono il piano e che dovrebbe essere in una posizione migliore per ottenere registrazioni idonee per i test. Come affermato in precedenza, le prime versioni di questo sistema saranno utilizzate per semplici registrazioni monofoniche, eventualmente procedendo in seguito a input più complessi a seconda dei progressi compiuti nelle prossime settimane.

Ecco un grafico che illustra l’approccio a soglia per rilevare la rilevazione di insorgenza:

alt text

Questa immagine mostra un tipico file WAV con tre note discrete riprodotte in successione. La linea rossa rappresenta una soglia di segnale scelta, e le linee blu rappresentano le posizioni di inizio della nota restituite da un semplice algoritmo che segna un inizio quando il livello del segnale supera la soglia.

Come mostra l’immagine, la selezione di una corretta soglia assoluta è difficile. In questo caso, la prima nota viene prelevata correttamente, la seconda nota viene completamente ignorata e la terza nota (appena) viene avviata molto tardi. In generale, una soglia bassa ti porta a prendere note fantasma, mentre alzarla ti fa perdere le note. Una soluzione a questo problema consiste nell’utilizzare una soglia relativa che innesca un avvio se il segnale aumenta di una certa percentuale in un determinato intervallo di tempo, ma questo ha i suoi problemi.

Una soluzione più semplice consiste nell’utilizzare la compressione con il nome in qualche modo controintuitivo ( non la compressione MP3, che è qualcos’altro interamente ) prima sul file wave. La compressione essenzialmente appiattisce i picchi nei dati audio e quindi amplifica tutto in modo che più dell’audio sia vicino ai valori massimi. L’effetto sul campione sopra sarebbe simile a questo (che mostra perché il nome “compressione” sembra non avere senso – sulle apparecchiature audio è solitamente etichettato come “volume”):

alt text

Dopo la compressione, l’approccio della soglia assoluta funzionerà molto meglio (anche se è facile comprimere eccessivamente e iniziare a raccogliere note di finzione, lo stesso effetto dell’abbassare la soglia). Ci sono un sacco di editor di wave che fanno un buon lavoro di compressione, ed è meglio lasciarli gestire questo compito – probabilmente dovrai fare una discreta quantità di lavoro “ripulendo” i tuoi file wave prima di rilevare le note in loro comunque.

In termini di codifica, un file WAV caricato in memoria è essenzialmente solo una matrice di numeri interi a due byte, dove 0 non rappresenta alcun segnale e 32.767 e -32.768 rappresentano i picchi. Nella sua forma più semplice, un algoritmo di rilevamento della soglia dovrebbe iniziare dal primo campione e leggere l’array fino a quando non trova un valore maggiore della soglia.

short threshold = 10000; for (int i = 0; i < samples.Length; i++) { if ((short)Math.Abs(samples[i]) > threshold) { // here is one note onset point } } 

In pratica funziona in modo orribile, poiché l’audio normale ha tutti i tipi di picchi transitori sopra una determinata soglia. Una soluzione consiste nell’utilizzare una potenza del segnale media corrente (cioè non contrassegnare un inizio finché la media degli ultimi n campioni non supera la soglia).

 short threshold = 10000; int window_length = 100; int running_total = 0; // tally up the first window_length samples for (int i = 0; i < window_length; i++) { running_total += samples[i]; } // calculate moving average for (int i = window_length; i < samples.Length; i++) { // remove oldest sample and add current running_total -= samples[i - window_length]; running_total += samples[i]; short moving_average = running_total / window_length; if (moving_average > threshold) { // here is one note onset point int onset_point = i - (window_length / 2); } } 

Tutto ciò richiede molto tweaking e gioco con le impostazioni per farlo trovare con precisione le posizioni iniziali di un file WAV, e di solito ciò che funziona per un file non funzionerà molto bene su un altro. Questo è un dominio problematico molto difficile e non perfettamente risolto che hai scelto, ma penso che sia bello che lo stai affrontando.

Aggiornamento: questo grafico mostra un dettaglio della rilevazione delle note che ho tralasciato, cioè il rilevamento al termine della nota:

alt text

La linea gialla rappresenta il fuori soglia. Una volta che l’algoritmo ha rilevato l’inizio di una nota, si presume che la nota continui fino a quando la potenza del segnale media corrente scende al di sotto di questo valore (mostrato qui dalle linee viola). Questa è, naturalmente, un’altra fonte di difficoltà, come nel caso in cui due o più note si sovrappongono (polifonia).

Una volta individuati i punti di inizio e di fine di ciascuna nota, puoi ora analizzare ogni sezione di dati del file WAV per determinare le tonalità.

Aggiornamento 2: ho appena letto la tua domanda aggiornata. Il rilevamento del passo tramite auto-correlazione è molto più facile da implementare rispetto a FFT se stai scrivendo da zero, ma se hai già verificato e utilizzato una libreria FFT pre-costruita, è meglio usarlo di sicuro . Una volta identificate le posizioni di inizio e di fine di ogni nota (e incluse alcune imbottiture all’inizio e alla fine per l’attacco mancato e le parti di rilascio), è ora ansible estrarre ciascuna porzione di dati audio e passarla a una funzione FFT per determinare il tono.

Un punto importante qui non è usare una fetta dei dati audio compressi, ma piuttosto usare una porzione dei dati originali e non modificati. Il processo di compressione distorce l’audio e può produrre una lettura del passo inaccurata.

Un ultimo punto sui tempi di attacco delle note è che potrebbe essere un problema minore di quanto si pensi. Spesso nella musica uno strumento con un attacco lento (come un synth morbido) inizierà una nota prima di uno strumento di attacco acuto (come un pianoforte) ed entrambe le note suoneranno come se stessero iniziando nello stesso momento. Se stai suonando gli strumenti in questo modo, l’algoritmo con lo stesso tempo di inizio per entrambi i tipi di strumenti, che è buono da una prospettiva WAV-to-MIDI.

Ultimo aggiornamento (spero): Dimentica ciò che ho detto sull’inclusione di alcuni campioni di paddings dalla prima parte di attacco di ogni nota – ho dimenticato che questa è una pessima idea per il rilevamento del pitch. Le parti di attacco di molti strumenti (specialmente il pianoforte e altri strumenti di tipo percussivo) contengono transienti che non sono multipli del passo fondamentale e tendono a rovinare il rilevamento del passo. In realtà vuoi iniziare ogni fetta un po ‘dopo l’attacco per questo motivo.

Oh, e tipo di importante: il termine “compressione” qui non si riferisce alla compressione in stile MP3 .

Aggiorna di nuovo: ecco una semplice funzione che esegue la compressione non dynamic:

 public void StaticCompress(short[] samples, float param) { for (int i = 0; i < samples.Length; i++) { int sign = (samples[i] < 0) ? -1 : 1; float norm = ABS(samples[i] / 32768); // NOT short.MaxValue norm = 1.0 - POW(1.0 - norm, param); samples[i] = 32768 * norm * sign; } } 

Quando param = 1.0, questa funzione non avrà alcun effetto sull'audio. Valori di parametro più grandi (2.0 è buono, che quadrerà la differenza normalizzata tra ciascun campione e il valore di picco massimo) produrranno più compressione e un suono generale più forte (ma schifoso). I valori inferiori a 1.0 produrranno un effetto di espansione.

Un altro punto probabilmente ovvio: dovresti registrare la musica in una stanza piccola e non ecologica poiché gli echi vengono spesso rilevati da questo algoritmo come note fantasma.

Aggiornamento: ecco una versione di StaticCompress che verrà compilata in C # e l'ESPLICITÀ esegue il cast di tutto. Ciò restituisce il risultato previsto:

 public void StaticCompress(short[] samples, double param) { for (int i = 0; i < samples.Length; i++) { Compress(ref samples[i], param); } } public void Compress(ref short orig, double param) { double sign = 1; if (orig < 0) { sign = -1; } // 32768 is max abs value of a short. best practice is to pre- // normalize data or use peak value in place of 32768 double norm = Math.Abs((double)orig / 32768.0); norm = 1.0 - Math.Pow(1.0 - norm, param); orig = (short)(32768.0 * norm * sign); // should round before cast, // but won't affect note onset detection } 

Spiacente, il mio punteggio di conoscenza su Matlab è 0. Se hai postato un'altra domanda sul perché la tua funzione Matlab non funziona come previsto, otterrebbe una risposta (solo non da me).

Quello che vuoi fare è spesso chiamato WAV-to-MIDI (google “wav-to-midi”). Ci sono stati molti tentativi in ​​questo processo, con risultati variabili (nota che l’esordio è una delle difficoltà, la polifonia è molto più difficile da gestire). Ti consiglio di iniziare con una ricerca approfondita delle soluzioni standard e iniziare a lavorare da solo se non c’è nulla di accettabile.

L’altra parte del processo di cui avresti bisogno è qualcosa per rendere l’output MIDI come una partitura musicale tradizionale, ma ci sono mille miliardi di prodotti che lo fanno.

Un’altra risposta è: sì, ho fatto un sacco di elaborazione del segnale digitale (vedi il software sul mio sito web – è un sintetizzatore software a voce infinita scritto in VB e C), e sono interessato ad aiutarti con questo problema. La parte WAV-to-MIDI non è poi così difficile da concettualizzare, è solo farla funzionare in modo affidabile nella pratica che è difficile. L’inizio della nota sta semplicemente impostando una soglia: gli errori possono essere facilmente regolati in avanti o indietro nel tempo per compensare le differenze di attacco delle note. Il rilevamento dell’intonazione è molto più facile da eseguire su una registrazione rispetto a quanto avviene in tempo reale e implica solo l’implementazione di una routine di auto-correlazione.

Dovresti guardare a MIRToolbox – è scritto per Matlab e ha un rilevatore di insorgenza integrato – funziona abbastanza bene. Il codice sorgente è GPL, quindi puoi implementare l’algoritmo in qualunque lingua funzioni per te. In quale lingua verrà utilizzato il tuo codice di produzione?

questa libreria è incentrata sull’etichettatura audio:

aubio

aubio è una libreria per l’etichettatura audio. Le sue caratteristiche includono la segmentazione di un file audio prima di ogni attacco, l’esecuzione del rilevamento del pitch, il ritmo del beat e la produzione di flussi midi dall’audio live. Il nome aubio proviene da ‘audio’ con un errore di battitura: è probabile che diversi errori di trascrizione vengano trovati anche nei risultati.

e ho avuto fortuna con esso per il rilevamento delle fasi e il rilevamento del pitch. È in c, ma ci sono wrapper swig / python.

inoltre, l’autore della biblioteca ha un pdf della sua tesi sulla pagina, che ha ottime informazioni e informazioni sull’etichettatura.

Gli hardset sono facilmente rilevabili nel dominio del tempo usando una misurazione dell’energia media.

SUM da 0 a N (X ^ 2)

Fatelo con pezzi dell’intero segnale. Dovresti vedere i picchi quando si verificano gli onsets (la dimensione della finestra dipende da te, il mio suggerimento è 50ms o più).

Documenti completi sul rilevamento di insorgenza:

Per gli ingegneri Hardcore:

http://www.nyu.edu/classs/bello/MIR_files/2005_BelloEtAl_IEEE_TSALP.pdf

Più facile per una persona media capire:

http://bingweb.binghamton.edu/~ahess2/Onset_Detection_Nov302011.pdf

Potresti provare a trasformare il segnale wav in un grafico di ampiezza rispetto al tempo. Quindi un modo per determinare un inizio consistente consiste nel calcolare l’intersezione di una tangente nel punto di flesso del fianco ascendente di un segnale con l’asse x.