Song Identifier

Introducere

Shazam este un program ce se bazează pe înregistrarea și analiza sunetelor pentru a recunoaște cântece folosind microfonul telefonului mobil.

Proiectul constă în realizarea unui astfel de program ce poate rula pe un mediu constrâns cum ar fi Raspberry Pico, programat folosind framework-ul embassy-rs în Rust C. Am decis să folosesc C deoarece am întâmpinat multe probleme cu embassy deoarece nu aveam un debug probe.

Algoritmul de identificare al cântecelor se folosește de FFT, algoritm ce convertește un semnal din domeniul timpului în domeniul frecvenței, astfel obținându-se frecvențele specifice ale fiecărui cântec.

Descriere generală

Diagramă bloc

La apăsarea unui buton, dispozitivul începe să înregistreze 10 secunde de audio pe care apoi le analizează folosind FFT.

Ideea de bază este că undele ce formează un cântec sunt funcții ce depind de timp. Folosind FFT putem să le transformăm în funcții ce depind de frecvențe – astfel, fiecare undă devine indentificabilă. Pentru oameni, aceste frecvențe sunt vizibile folosind o spectrogramă. Aceasta se poate obține împărțind melodia în secvențe scurte, aplicând FFT pe fiecare, obținând “slice-uri” ce pot fi apoi concatenate pentru a obține acest al varianței frecvențelor în funcție de timp. Culorile deschise reprezintă o intensitate mai mare a anumitor frecvențe.

Possible Rick Roll Ahead!

Din spectrograma cântecului Never Gonna Give You Up putem deduce câteva lucruri interesante:

- deși spectrul auditiv este considerat 20-20000Hz+, majoritatea cântecelor nu vor conține frecvențe mai mari de 16000Hz.

- frecvențele joase au o intensitate mai mare în cântece, deoarece ele sunt percepute ca fiind mai puțin intense ca sunetele înalte.

Unele cântece pot conține “desene” în spectrograme. Funcționează în special deoarece anumite frecvențe sunt foarte slab auzibile de om și, odată cu vârsta, omul își pierde capacitatea de a auzi anumite frecvențe înalte.

Sursa

Hardware Design

Listă de piese:

  • Raspberry PI Pico
  • Microfon MAX4466
  • Cablu USB
  • Fire DuPont
  • Breadboard
  • Buton
  • Ecran LCD 1602

Schematic:

Descriere pini:

Dispozitiv Funcție Pin Pico W Observații
LCD I2C (PCF8574) GND GND GND comun
LCD I2C (PCF8574) VCC 3V3 (OUT) Alimentare 3.3V
LCD I2C (PCF8574) SDA GP0 I2C0 SDA (folosește pull-up intern)
LCD I2C (PCF8574) SCL GP1 I2C0 SCL (folosește pull-up intern)
MAX4466 (microfon) GND GND GND comun
MAX4466 (microfon) VCC 3V3 (OUT) Alimentare 3.3V
MAX4466 (microfon) OUT GP26 (ADC0) Conectat la un pin ADC pentru citirea semnalului
Buton GND GND
Buton GPIO GP15

[ Buton ]
   |
   v
[ Pico W detectează apăsarea ]
   |
   v
[ MAX4466 captează sunetul ambiental ]
   |
   v
[ Semnal analogic intră pe GP26 (ADC) ]
   |
   v
[ Pico W face sampling și FFT ]
   |
   v
[ Căutare în hashmap a hashului fiecărui sample din înregistare ]
   |
   v
[ Rezultat afișat pe LCD 16x2 via I2C ]

Software Design

Ecran LCD, Butoane, Microfon

LCD-ul este programat folosind interfața I2C. Am făcut un fișier ce expune o interfață pentru scris pe ecran.

Microfonul MAX4466 este conectat prin interfața ADC. Pico dispune de un ADC pe 12 biți, așa că am decis să folosesc 8 biți pentru înregistrare(formatele audio pe 12 biți nu sunt un standard).

FFT

Cea mai complexă parte a codului reprezintă înregistrarea și prelucrarea input-ului audio pentru recunoașterea cântecului. Aplicând Nyquist–Shannon sampling theorem, aflăm că pentru a obține frecvențe din intervalul 0-x Hz, trebuie să facem sampling în intervalul 0-2*x Hz. Fiecare sample înregistrat este apoi împărțit în mai multe linii de frecvențe din care selectăm cele mai puternice sunete din fiecare bandă de frecvență. Am ales să aleg 4 cele mai puternice frecvențe pentru a crea un hash de 4 bytes pentru fiecare sample.

FFT-ul este implementat folosind biblioteca KissFFT.

Prima problemă pe care o întâmpinăm este faptul că PICO are doar 512KB de SRAM si 4 MB flash. Dacă am folosi tot acest spațiu și un cântec ar avea în medie 4000 de samples(un sample la 50 de milisecunde pentru un cântec de 200 de secunde), am avea nevoie de 16K de memorie pentru un singur cântec, deci am putea stoca maxim 256 de cântece în flash / 32 de cântece în SRAM. În plus, am vrea să putem înregistra 10 secunde dintr-un cântec unde aplicăm sampling la 16000Hz, am avea nevoie de 160KB, în plus doar pentru a înregistra un sample.

Planul devine în mod rapid nefezabil așa că avem nevoie de tactici pentru a reduce consumul mare de memorie. Ce am făcut a fost să scad samplerate-ul până la 800Hz. Semnăturile digitale sunt precomputate și puse în memoria microcontrollerului ca apoi să fie matched.

Pseudocod: Recunoaștere melodie cu Raspberry Pi Pico W
  * Inițializează periferice:
    * I2C pentru LCD
    * ADC pentru microfon (ex: GP26 / ADC0)
    * GPIO pentru buton


while true:
    dacă butonul este apăsat:
        afișează pe LCD "Ascult..."
        
        // 1. Eșantionare semnal audio
        colectează N mostre de la ADC (durată: ~5 secunde)
        salvează datele într-un buffer
        
        // 2. Preprocesare
        aplică fereastră Hanning (sau altă fereastră) peste date
        
        // 3. Aplică FFT
        execută transformata Fourier rapidă (FFT) pe buffer
        obține spectrul de frecvență
        
        // 4. Generează semnătură
        extrage caracteristici dominante (peak-uri, forma generală, etc.)
        generează un hash/structură cu semnătura audio
        
        // 5. Compară cu baza de date
        pentru fiecare semnătură salvată în baza de date:
            dacă semnătura se potrivește:
                afișează pe LCD: "Melodie găsită:"
                afișează numele melodiei
                break
        dacă nicio potrivire:
            afișează "Melodie necunoscută"

Setup -- Obținerea hash-urilor

Pentru a obține hash-urile am scris un program în C++ care, folosind aceeași algoritmi ca și codul pus pe Pico, generează hash-urile cântecelor ce sunt apoi încărcate pe pico înainte de rulare.

Pentru a obține cântecele le-am descărcat de pe youtubeyt-dlp, le-am convertit într-un format necompresat (.wav) pe 1 byte la acelasi sample rate ca cel înregistrat de microcontroller folosind ffmpeg și le-am dat ca date de intrare pentru program.

Proiectul este dezvoltat folosind VSCode. Pentru compilarea proiectului am folosit ninja și cmake. Pentru flashing am folosit picotool.

Rezultate Obţinute

Demo

Dispozitivul este capabil de a recunoaște cântece, deși cu o precizie destul de slabă. Sunt multe motive pentru care acesta ar putea eșua: zgomote puternice în timpul înregistrării, fidelitatea înregistrării scăzute, coliziuni de hash tabela ce ține frecvențele, etc.

Concluzii

Proiectul funcționează, deși nu la fel de bine precum am sperat.

Consider mai multe îmbunătățiri pe care le-aș putea face(micșorarea benzilor de frecvență, modificarea relației pentru semnătura cântecului, etc.) dar acestea necesită o perioada îndelungată de cercetare și testare pentru a ajunge la rezultatul dorit.

Dacă ești student de anul 3 și ai vrea să continui de unde am început eu, nu ezita să mă contactezi: matei.mantu1@gmail.com

Download

Bibliografie/Resurse

pm/prj2025/eradu/matei.mantu.txt · Last modified: 2025/05/29 22:10 by matei.mantu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0