Yusuf Bülbül

An Engineer

Ses İzi(Sound Fingerprint) ve Kodlar

Sanırım herkes Shazam uygulamasını biliyordur. İsmini bilmediğiniz bir şarkı duyduğunuz da bu uygulama ile şarkının hemen ismini öğrenebiliyorsunuz. Bu yazıda bu uygulamanın arka planda hangi bilimsel yöntemleri kullandığını ve böyle bir uygulamayı kendiniz yazmak istediğinde örnek kodlar üzerinden bu konuyla ilgili açık kaynak kodlu projeleri ele alacağım.

Shazam uygulaması ses izi(audio Fingerprinting)  denilen yönetimi kullanıyor.  Bu yöntemle sesler, karşılaştırılabilecek forma dönüştürülüyor ve ismini bilmek istediğiniz bir şarkının kısa bir kesitinin ses izi alınarak, veri tabanına kaydedilen yüz binlerce ses izi ile kıyaslanıyor. Ve en çok benzerlik gösteren şarkı, sonuç olarak karşınıza dönüyor.

Ses izi çıkarmanın bir kaç yönetimi vardır.  Bu yöntemler,  farklı ses tiplerine göre farklı performansa sahipler. Örneğin bazı yöntemler, çevresel gürültü dışındaki saf ses izine yoğunlaşırken bazıları, sesin değişen çalma hızı ve tonlarına yoğunlaşıyor.  Shazam uygulamasının kullandığı yöntem aslında patentlenmiş ve gizlenmiş bir yöntem olsa da aslında mevcutta olan yöntemlerden çok farklı bir yöntem olmadığı aşikar görünüyor.

Temel olarak ses izi algoritması,  sesin frekans ve zaman uzayındaki  grafiğini baz alıyor.  Bu grafiğin ismine “spectogram” deniliyor. Sesin bu uzaya çevrilip bu bilgilerin hash yani özet dijital bilgi haline getirilmesi ile aslında ses izi alınmış oluyor.  Yani, ses izi, yapısal olarak, ses verisini zaman uzayında en iyi bir şekilde tanımlayabilecek digital bilgidir.

Parmak izi çıkarmanın, ilk yöntemi “Robust Constellation” dır. Bu yöntem temel olarak ses spectogramının, peak yani tepe noktalarının bilgisinin çıkarılmasına dayanır. Spectogramdaki bu tepe noktaları sesin enerjisi anlamına geliyor aslında.  Yani frekansının yoğun olduğu noktalar ses hakkındaki en fazla bilgiyi içeren noktalar oluyor. Sadece Spectogramdaki bu tepe noktaları  dijital bilgiye çevirilerek “Constellation map” denilen bilgi elde ediliyor. Bu yöntem aslında ses bilgisinin daha küçük çerçeveler haline getirmenin ilk aşamasıdır.

Yazılım dünyasında verileri özetlemek ve en kısa ve en belirgin şekilde sokmak için hash alma yöntemi kullanılır. Hash aslında bir dijital verinin bir daha sıkıştırılmış ve kodlanmış halidir.  Matematiksel olarak bir sesin “spectogram” ya da “constallation map” bilgini dijital olarak çıkarıp kaydettiğinizde, oldukça büyük ve çok yer kaplayan veri elde edersiniz. Bu verinin “hash”‘ ni aldığınızda  bu dijital verinin bütününe özel benzersiz kısa bir özet veri elde edersiniz. Buna ise temel olarak ses izi diyebiliriz. İki dijital verinin “hash” yani özetlerini kıyaslayarak olasılıksal olarak verilerin yüzde kaçının birbirine benzer olduğunu çıkartabiliyorsunuz.  Shazam uygulamasının kullandığı en temel mantık da budur.  Bu yazıda “hash” alma algoritmasının daha derinine değinmeyeceğim. Bu konu hakkındaki daha derin bilgileri, internetten aratarak bulabilirsiniz.

İşin bu kısmından sonrası, ismini aradığınız sesin, ses izini çıkarıp milyonlarca ses iziyle kıyaslayarak doğru ismi bulabilmeyi en kısa sürede nasıl yapacağınıza kalıyor.  Bu da oldukça güçlü bir arama ve hash algoritması ve veritabanı yönetim sistemine bağlı oluyor.

şimdi geçelim işin kod kısmına. Bu algoritmaları kendiniz koda uyarlamaya çalışırsanız tabi ki bu konu hakkında teorik olarak oldukça fazla bilgi, birikim ve teorik bilgiye ihtiyacınız olacak. Bunun yerine zaten hali hazırda açık kaynak olarak geliştirilmiş projelere yönelmek en mantıklısı.

Bu konuda önerebileceğim en kapsamlı ve en sağlam açık kaynak kodlu proje dotnet framework 2.0 sürümü kullanarak yazılmış şu proje, https://github.com/AddictedCS/soundfingerprinting

Projeyi  şu komutla kendi makinenize indirin.

git clone https://github.com/AddictedCS/soundfingerprinting.git

Tabi bu kod çalıştırılabilir bir proje değil. Bunu derleyip kendi yazacağınız kod referans olarak göstermeniz gerekiyor. Tabi öncelikle linux bilgisayarınıza dotnet framework 2.1 i indirip kurmanız gerekiyor. Bunun için şu kodları kullanabilirsiniz;

sudo apt-get update
sudo apt-get install apt-transport-https
sudo apt-get update
sudo apt-get install dotnet-sdk-2.1
sudo apt-get install aspnetcore-runtime-2.1
sudo apt-get install dotnet-runtime-2.1

Bunları kurduktan sonra, kaynak klasörü içerisinde projeyi derleyin ve kendi oluşturduğunuz dotnet projesine referans olrak gösterin. Bunları şu kodlarla yapıyorsunuz;

./build.sh // build source code of library.
dotnet add app/app.csproj reference lib/lib.csproj //add referance to your new project
dotnet build //build your project

Daha sonra basit bir uygulama yapmak için şu kodları kullanabilirsiniz;

using System;

using SoundFingerprinting;
using SoundFingerprinting.Utils;
using SoundFingerprinting.Audio;
using SoundFingerprinting.Query;
using SoundFingerprinting.Configuration;
using SoundFingerprinting.DAO;
using SoundFingerprinting.DAO.Data;
using SoundFingerprinting.Data;
using SoundFingerprinting.Strides;
using SoundFingerprinting.Math;
using SoundFingerprinting.LCS;
using SoundFingerprinting.InMemory;
using SoundFingerprinting.Image;
using SoundFingerprinting.FFT;
using SoundFingerprinting.Command;
using SoundFingerprinting.Builder;
using System.Threading.Tasks;
using System.Linq;

namespace newproj
{
    public class test
    {
        
        private readonly IModelService modelService = new InMemoryModelService(); // store fingerprints in RAM

        private readonly IAudioService audioService = new SoundFingerprintingAudioService(); // default audio library
        private static Random random = new Random();
        public static string RandomString(int length)
        {
            const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            return new string(Enumerable.Repeat(chars, length)
            .Select(s => s[random.Next(s.Length)]).ToArray());
        }

        public async Task insertFingerPrint(string pathToAudioFile)
        {
            
            var track = new TrackInfo(RandomString(10), pathToAudioFile, "Med");

            // create fingerprints
            var hashedFingerprints = await FingerprintCommandBuilder.Instance
                                        .BuildFingerprintCommand()
                                        .From(pathToAudioFile)
                                        .UsingServices(audioService)
                                        .Hash();

            modelService.Insert(track, hashedFingerprints);

        }

        public async Task<TrackData> GetBestMatchForSong(string queryAudioFile)
        {
            int secondsToAnalyze = 30; // number of seconds to analyze from query file
            int startAtSecond = 0; // start at the begining


            // query the underlying database for similar audio sub-fingerprints
            var queryResult = await QueryCommandBuilder.Instance.BuildQueryCommand()
                                                .From(queryAudioFile, secondsToAnalyze, startAtSecond)
                                                .UsingServices(modelService, audioService)
                                                .Query();

            Console.WriteLine("secondsToAnalyze " + secondsToAnalyze.ToString()) ;
            Console.WriteLine("startAtSecond: " + startAtSecond.ToString());
            Console.WriteLine("TrackMatchStartsAt : " + queryResult.BestMatch.TrackMatchStartsAt.ToString());



            return queryResult.BestMatch.Track;
        }

    }

    class Program
    { 
        static async Task Main(string[] args)
        {
            int counter = 0;  
            string line;  
            test t = new test();

            // Read the file and display it line by line.  
            System.IO.StreamReader file =   
                new System.IO.StreamReader(@"source.txt");  
            while((line = file.ReadLine()) != null)  
            {  
                bool s1 =  String.IsNullOrEmpty(line);

                if(!s1) 
                {
                    System.Console.WriteLine("Song added: " + line);  

                    await t.insertFingerPrint(line);

                    counter++;  
                }

            }  
            

            System.Console.WriteLine("Song searching ");  

            var track = await t.GetBestMatchForSong("example.wav");

            Console.WriteLine("result : " + track.Title.ToString());

        }


    }
}

Bu program “source.txt” isimli bir text dosyasından ilgili şarkıları okuyup bir hash veri tabanı oluşturuyor. Daha sonra herhangi bir müzik dosyası kesitini bu veri tabanından sorgulatıyor. Proje ile ilgili daha fazla detayı github sayfasında bulabilirsiniz.

C# sevmiyorsanız,  C ile yazılmış bir diğer açık kaynak projesi daha mevcut. O proje de şu;  https://github.com/acoustid/chromaprint

Chromaprint projesini bu kodlarla indirip derleyin ve sistem dizinine yükleyin;

git clone https://github.com/acoustid/chromaprint
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TOOLS=ON .
make
sudo make install

Daha sonra basit bir ses dosyasının ses izini çıkarmak için şu kodları kullanabilirsiniz;

    ChromaprintContext *ctx;
    char *fp;
    const int sample_rate = 44100;
    const int num_channels = 2;
    ctx = chromaprint_new(CHROMAPRINT_ALGORITHM_DEFAULT);
    chromaprint_start(ctx, sample_rate, num_channels);
    while (has_more_data) {
        chromaprint_feed(ctx, data, size);
    }
    chromaprint_finish(ctx);
    chromaprint_get_fingerprint(ctx, &fp);
    printf("%s\n", fp);
    chromaprint_dealloc(fp);
    chromaprint_free(ctx);

 

Chromaprint ses izi çıkarmak için kullanılan ve AcoustID projesinin temelinde kullanılan bir açık kaynak kodlu projedir.  Giriş olarak raw (“16-bit signed int) ses verisi alır.  Giriş sesi,  herhangi bir “sample rate” ya da kanalda olabilir. Normalde bu veri girişini sağlamak için dosyadan veriyi okumak ve sıkıştırılmış ses verisini raw haline getirmek için başka bir kütüphane kullanmanız gerekiyor.

Bu kütüphane, sonuç olarak base64 ile kodlanmış “32 bit array” dönüyor.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Copyright © Tüm Hakları Saklıdır.