defmodule ChromaprintTest do use ExUnit.Case, async: true import Bitwise doctest Chromaprint @sample_rate 44_100 # Chromaprint needs ~3+ seconds of audio to produce a fingerprint. @duration_seconds 4 defp sine_pcm(freq_hz, duration_s, sample_rate) do n = duration_s * sample_rate two_pi = 2.0 * :math.pi() for i <- 0..(n - 1), into: <<>> do sample = trunc(:math.sin(two_pi * freq_hz * i / sample_rate) * 32_000) <> end end test "library version is reported" do assert is_binary(Chromaprint.version()) assert byte_size(Chromaprint.version()) > 0 end test "lists supported algorithms" do assert :test2 in Chromaprint.algorithms() end describe "streaming API" do setup do {:ok, pcm: sine_pcm(440, @duration_seconds, @sample_rate)} end test "produces a non-empty fingerprint for a sine wave", %{pcm: pcm} do {:ok, ctx} = Chromaprint.new() :ok = Chromaprint.start(ctx, sample_rate: @sample_rate, channels: 1) :ok = Chromaprint.feed(ctx, pcm) :ok = Chromaprint.finish(ctx) assert {:ok, fp} = Chromaprint.fingerprint(ctx) assert is_binary(fp) assert byte_size(fp) > 0 end test "raw fingerprint is a non-empty list of uint32", %{pcm: pcm} do {:ok, ctx} = Chromaprint.new() :ok = Chromaprint.start(ctx, sample_rate: @sample_rate, channels: 1) :ok = Chromaprint.feed(ctx, pcm) :ok = Chromaprint.finish(ctx) assert {:ok, raw} = Chromaprint.raw_fingerprint(ctx) assert is_list(raw) assert length(raw) > 0 assert Enum.all?(raw, fn x -> is_integer(x) and x >= 0 and x < 1 <<< 32 end) end test "hash returns a 32-bit unsigned integer", %{pcm: pcm} do {:ok, ctx} = Chromaprint.new() :ok = Chromaprint.start(ctx, sample_rate: @sample_rate, channels: 1) :ok = Chromaprint.feed(ctx, pcm) :ok = Chromaprint.finish(ctx) assert {:ok, h} = Chromaprint.hash(ctx) assert is_integer(h) and h >= 0 and h < 1 <<< 32 end end describe "compute/2 one-shot" do test "matches the streaming result on the same input" do pcm = sine_pcm(440, @duration_seconds, @sample_rate) {:ok, ctx} = Chromaprint.new() :ok = Chromaprint.start(ctx, sample_rate: @sample_rate, channels: 1) :ok = Chromaprint.feed(ctx, pcm) :ok = Chromaprint.finish(ctx) {:ok, streamed} = Chromaprint.fingerprint(ctx) assert {:ok, ^streamed} = Chromaprint.compute(pcm, sample_rate: @sample_rate, channels: 1) end end describe "encode/2 and decode/2 round-trip" do test "raw → encode → decode → raw" do pcm = sine_pcm(440, @duration_seconds, @sample_rate) {:ok, ctx} = Chromaprint.new() :ok = Chromaprint.start(ctx, sample_rate: @sample_rate, channels: 1) :ok = Chromaprint.feed(ctx, pcm) :ok = Chromaprint.finish(ctx) {:ok, raw} = Chromaprint.raw_fingerprint(ctx) {:ok, fp} = Chromaprint.fingerprint(ctx) assert {:ok, ^fp} = Chromaprint.encode(raw, algorithm: :test2, base64: true) assert {:ok, %{algorithm: :test2, raw: ^raw}} = Chromaprint.decode(fp) end end end