Skip to content

Emy Storage

Sergiu Ciumac edited this page Dec 24, 2021 · 10 revisions

Emy storage

Emy storage was implemented after multiple requests for persistent storage for audio fingerprints generated by the SoundFingerprinting library. It powers emysound.com, a commercial cloud version of it. Almost everything available in the cloud is also available in the Docker community edition. The community edition is free for non-commercial use.

Run Emy community edition in Docker

First thing, you need to start Docker container for Emy.

docker run -d -p 3399:3399 -p 3340:3340 addictedcs/soundfingerprinting.emy:latest

Once it is started, you can access the dashboard at the following address: http://localhost:3340.

Emysound home page

The Swagger for the JSON API that Emy offers is available on http://localhost:3340/docs.

The next thing you can do is insert tracks, using the tracks page.

Tracks page

Once inserted, you can start monitoring external broadcasts using the streams page. By enabling the checkbox Save audio for matches playback, Emy will store matched regions that you will be able to playback on the Query Match Page.

Streams page

Query matches page will show all matches that happened on any particular stream.

Query matches page

Programmatic .NET insert and query Emy storage

Inserting tracks into Emy

The below program will insert a list of tracks from the specified directory into Emy. Please note that it uses SoundFingerprinting.Emy.

Create a new .NET Core app.

dotnet new console -f net6.0 -n Emy.Test

To install the NuGet package, navigate into the project directory cd Emy.Test and use the following command:

dotnet add package SoundFingerprinting.Emy

Copy the following program into Program.cs.

namespace Emy.Test
{
    using System;
    using System.IO;
    using System.Threading.Tasks;
    using SoundFingerprinting.Audio;
    using SoundFingerprinting.Builder;
    using SoundFingerprinting.Data;
    using SoundFingerprinting.Emy;

    class Program
    {
        private static readonly EmyModelService ModelService = EmyModelService.NewInstance("localhost", 3399);
        private static readonly IAudioService AudioService = new FFmpegAudioService();
        
        static async Task Main(string[] args)
        {
            if (args.Length < 1)
            {
                Console.WriteLine("Specify directory for inserting data");
                return;
            }
            
            
            var directory = args[0];
            foreach (string file in Directory.GetFiles(directory))
            {
                if (ModelService.ReadTrackById(file) != null)
                {
                    Console.WriteLine($"We've already inserted {file}. Skipping.");
                    continue;
                }
                
                var track = new TrackInfo(file, Path.GetFileNameWithoutExtension(file), string.Empty);
                var hashes = await FingerprintCommandBuilder
                    .Instance
                    .BuildFingerprintCommand()
                    .From(file)
                    .UsingServices(AudioService)
                    .Hash();

                ModelService.Insert(track, hashes);
                Console.WriteLine($"Inserted {file} with {hashes.Count} fingerprints.");
            }
        }
    }
}

Compile the project (from the same Emy.Test directory).

dotnet clean -c Release && dotnet build -c Release

Now you are ready to run it. Specify the directory with files as a program argument.

cd bin/Release/net6.0
dotnet Emy.Test.dll <PATH_TO_DIRECTORY_WITH_AUDIO_FILES>

You can navigate into the Tracks tab at http://localhost:3340/tracks and check if the files are getting inserted.

FFmpegAudioService is used in this example since it's the best decoding and downsampling implementation. For those who don't know ffmpeg is an external library that has to be installed on the running machine. Installation steps can be found here.

Querying Emy

Similar to insertion, in order to query you can use the following code snippet.

static async Task Query(string pathToQueryFile)
{
    var result = await QueryCommandBuilder.Instance
        .BuildQueryCommand()
        .From(pathToQueryFile)
        .UsingServices(ModelService, AudioService)
        .Query();

    if (result.ContainsMatches)
    {
        foreach (var (entry, _) in result.ResultEntries)
        {
            if (entry?.Confidence > 0.3)
            {
                Console.WriteLine($"Found {entry.Track.Id} with coverage {entry.TrackRelativeCoverage}");
                Console.WriteLine($"Query match starts at: {entry.QueryMatchStartsAt:0.00}");
                Console.WriteLine($"Track match starts at: {entry.TrackMatchStartsAt:0.00}");

                ModelService.RegisterMatches(new []{entry}, new Dictionary<string, string>());
            }
        }
    }
}

Note how we register the match by calling RegisterMatches. This will persist match information in Emy storage. Navigate to http://localhost:3340/matches Matches tab to view the latest query matches.

List of other available storage is available here.

JSON interface

You can use Emy by taking advantage of its rich API interface available on http://localhost:3340/docs. The description of the available methods can be viewed here: https://emysound.readme.io