├── bindings └── wasm │ ├── src │ ├── vibra.js │ ├── vibra.wasm │ ├── cli.ts │ ├── index.ts │ ├── shazam.ts │ └── uger_agent.ts │ ├── build-wasm.sh │ ├── tsconfig.json │ ├── Dockerfile │ ├── package.json │ ├── Makefile │ ├── wasm.cpp │ ├── README.md │ └── package-lock.json ├── tests ├── sample.mp3 └── test.sh ├── docs ├── project_vibra.png ├── project_vibra_benchmark.png └── logo_license.svg ├── cli ├── main.cpp ├── cli.h ├── communication │ ├── shazam.h │ ├── timezones.h │ ├── shazam.cpp │ └── user_agents.h ├── utils │ └── uuid4.h ├── CMakeLists.txt └── cli.cpp ├── .gitattributes ├── CPPLINT.cfg ├── .github └── workflows │ ├── build-ci.yaml │ ├── build-macos-amd64.yaml │ ├── build-macos-arm64.yaml │ ├── build-linux-amd64.yaml │ ├── build-linux-arm64.yaml │ ├── build-windows-amd64.yaml │ └── build-webassembly.yaml ├── .gitignore ├── lib ├── utils │ ├── crc32.h │ ├── base64.h │ ├── ring_buffer.h │ ├── fft.h │ ├── ffmpeg.h │ └── hanning.h ├── algorithm │ ├── frequency.cpp │ ├── frequency.h │ ├── signature_generator.h │ ├── signature.h │ ├── signature.cpp │ └── signature_generator.cpp ├── audio │ ├── byte_control.h │ ├── downsampler.h │ ├── wav.h │ ├── wav.cpp │ └── downsampler.cpp ├── CMakeLists.txt └── vibra.cpp ├── .clang-format ├── CMakeLists.txt ├── include └── vibra.h ├── README.md └── LICENSE /bindings/wasm/src/vibra.js: -------------------------------------------------------------------------------- 1 | ../build/vibra.js -------------------------------------------------------------------------------- /bindings/wasm/src/vibra.wasm: -------------------------------------------------------------------------------- 1 | ../build/vibra.wasm -------------------------------------------------------------------------------- /tests/sample.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BayernMuller/vibra/HEAD/tests/sample.mp3 -------------------------------------------------------------------------------- /docs/project_vibra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BayernMuller/vibra/HEAD/docs/project_vibra.png -------------------------------------------------------------------------------- /docs/project_vibra_benchmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BayernMuller/vibra/HEAD/docs/project_vibra_benchmark.png -------------------------------------------------------------------------------- /cli/main.cpp: -------------------------------------------------------------------------------- 1 | #include "./cli.h" 2 | 3 | int main(int argc, char **argv) 4 | { 5 | CLI cli; 6 | return cli.Run(argc, argv); 7 | } 8 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.html linguist-vendored 2 | *.js linguist-vendored 3 | CMakeLists.txt linguist-vendored 4 | Makefile linguist-vendored 5 | *.h linguist-language=C++ -------------------------------------------------------------------------------- /CPPLINT.cfg: -------------------------------------------------------------------------------- 1 | set noparent 2 | linelength=100 3 | filter=-whitespace/newline,-whitespace/braces,-whitespace/parens,-whitespace/indent,-whitespace/comments,-legal/copyright 4 | exclude_files=./cli/args -------------------------------------------------------------------------------- /bindings/wasm/build-wasm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # build libvibra as wasm 4 | cd .. 5 | rm -rf build 6 | mkdir -p build 7 | cd build || exit 1 8 | 9 | emcmake cmake ../.. \ 10 | -DLIBRARY_ONLY=ON \ 11 | -DFFTW3_PATH=/usr/local \ 12 | -DCMAKE_INSTALL_PREFIX=/usr/local 13 | 14 | make -j 15 | sudo make install 16 | 17 | # build the wasm module 18 | cd ../wasm || exit 1 19 | rm -rf build 20 | mkdir -p build 21 | emmake make -------------------------------------------------------------------------------- /bindings/wasm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2016", 4 | "module": "commonjs", 5 | "rootDir": "./src", 6 | "allowJs": true, 7 | "sourceMap": true, 8 | "outDir": "./dist", 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "strict": true, 12 | "skipLibCheck": true 13 | }, 14 | "exclude": [ 15 | "build", 16 | "dist" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /docs/logo_license.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | The use of the Shazam logo in this project does not imply endorsement by Shazam or its parent company, Apple, Inc. 4 | The logo is used in accordance with company guidelines and for identification purposes only. 5 | 6 | 7 | -------------------------------------------------------------------------------- /bindings/wasm/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM emscripten/emsdk 2 | 3 | # Activate Emscripten SDK 4 | RUN emsdk install latest 5 | RUN emsdk activate latest 6 | 7 | ENV FFTW_VERSION=3.3.10 8 | ENV FFTW3_PATH=/usr/local 9 | RUN wget http://www.fftw.org/fftw-${FFTW_VERSION}.tar.gz && \ 10 | tar -xvf fftw-${FFTW_VERSION}.tar.gz && \ 11 | cd fftw-${FFTW_VERSION} && \ 12 | emconfigure ./configure --disable-fortran --prefix=${FFTW3_PATH} && \ 13 | emmake make -j9 && \ 14 | emmake make install 15 | 16 | WORKDIR /app -------------------------------------------------------------------------------- /cli/cli.h: -------------------------------------------------------------------------------- 1 | #ifndef CLI_CLI_H_ 2 | #define CLI_CLI_H_ 3 | 4 | #include 5 | #include "../include/vibra.h" 6 | 7 | class CLI 8 | { 9 | public: 10 | int Run(int argc, char **argv); 11 | 12 | private: 13 | Fingerprint *getFingerprintFromMusicFile(const std::string &music_file); 14 | Fingerprint *getFingerprintFromStdin(int chunk_seconds, int sample_rate, int channels, 15 | int bits_per_sample, bool is_signed); 16 | }; 17 | 18 | #endif // CLI_CLI_H_ 19 | -------------------------------------------------------------------------------- /.github/workflows/build-ci.yaml: -------------------------------------------------------------------------------- 1 | name: Build CI 2 | 3 | on: 4 | push: 5 | branches: [main, develop] 6 | pull_request: 7 | branches: [main, develop] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | precheck: 12 | runs-on: ubuntu-22.04 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v4 16 | 17 | - name: Install dependencies 18 | run: | 19 | sudo apt-get update 20 | sudo apt-get install -y build-essential cmake cpplint 21 | 22 | - name: Check cpplint 23 | run: cpplint --recursive lib include -------------------------------------------------------------------------------- /bindings/wasm/src/cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { Vibra } from "."; 4 | import { Shazam } from "./shazam"; 5 | import fs from 'fs' 6 | 7 | if (process.argv.length < 3) { 8 | console.log('Usage: node dist/cli.js '); 9 | process.exit(1); 10 | } 11 | const filePath = process.argv[2] 12 | 13 | const buffer = fs.readFileSync(filePath) 14 | 15 | const vibra = new Vibra(); 16 | vibra.on('initialized', () => { 17 | const signature = vibra.getSignature(buffer); 18 | Shazam.recognize(signature).then((result) => { 19 | console.log(JSON.stringify(result, null, 4)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore everything 2 | * 3 | 4 | # Unignore all files with extensions 5 | !*.* 6 | 7 | # Unignore all directories 8 | !*/ 9 | 10 | # Unignore makefile 11 | !Makefile 12 | 13 | *.o 14 | *.out 15 | .vscode/ 16 | __pycache__/ 17 | build/ 18 | 19 | # Unignore LICENSE 20 | !LICENSE 21 | 22 | # Ignore .DS_Store 23 | .DS_Store 24 | 25 | # Unignore cli/clipp 26 | !cli/args 27 | 28 | # Unignore Dockerfile 29 | !Dockerfile 30 | 31 | # Node.js 32 | node_modules/ 33 | npm-debug.log 34 | yarn-error.log 35 | yarn.lock 36 | 37 | # TypeScript 38 | *.tsbuildinfo 39 | dist/ 40 | 41 | # CLion 42 | .idea/ 43 | cmake-build-debug/ 44 | cmake-build-release/ -------------------------------------------------------------------------------- /bindings/wasm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vibra", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "bin": { 7 | "vibrajs": "dist/cli.js" 8 | }, 9 | "scripts": { 10 | "start": "node dist/cli.js", 11 | "build": "tsc && cp src/vibra.wasm dist/vibra.wasm", 12 | "prestart": "npm run build", 13 | "test": "echo \"Error: no test specified\" && exit 1" 14 | }, 15 | "author": "", 16 | "license": "ISC", 17 | "devDependencies": { 18 | "@types/axios": "^0.14.0", 19 | "@types/node": "^20.11.19", 20 | "@types/uuid": "^9.0.8", 21 | "axios": "^1.6.7", 22 | "typescript": "^5.3.3", 23 | "uuid": "^9.0.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /cli/communication/shazam.h: -------------------------------------------------------------------------------- 1 | #ifndef CLI_COMMUNICATION_SHAZAM_H_ 2 | #define CLI_COMMUNICATION_SHAZAM_H_ 3 | 4 | #include 5 | 6 | // forward declaration 7 | struct Fingerprint; 8 | // 9 | 10 | class Shazam 11 | { 12 | static constexpr char HOST[] = "https://amp.shazam.com/discovery/v5/fr/FR/android/-/tag/"; 13 | 14 | public: 15 | static std::string Recognize(const Fingerprint *fingerprint); 16 | 17 | private: 18 | static std::string getShazamHost(); 19 | static std::string getUserAgent(); 20 | static std::string getRequestContent(const std::string &uri, unsigned int sample_ms); 21 | static std::string getTimezone(); 22 | }; 23 | 24 | #endif // CLI_COMMUNICATION_SHAZAM_H_ 25 | -------------------------------------------------------------------------------- /lib/utils/crc32.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_UTILS_CRC32_H_ 2 | #define LIB_UTILS_CRC32_H_ 3 | 4 | namespace crc32 5 | { 6 | std::uint32_t crc32(const char *buf, std::size_t len) 7 | { 8 | std::uint32_t crc_table[256]; 9 | std::uint32_t crc; 10 | std::size_t i, j; 11 | 12 | for (i = 0; i < 256; i++) 13 | { 14 | crc = i; 15 | for (j = 0; j < 8; j++) 16 | crc = crc & 1 ? (crc >> 1) ^ 0xEDB88320UL : crc >> 1; 17 | crc_table[i] = crc; 18 | }; 19 | crc = 0xFFFFFFFFUL; 20 | 21 | while (len--) 22 | crc = crc_table[(crc ^ *buf++) & 0xFF] ^ (crc >> 8); 23 | 24 | return crc ^ 0xFFFFFFFFUL; 25 | } 26 | } // namespace crc32 27 | 28 | #endif // LIB_UTILS_CRC32_H_ 29 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # Language 2 | Language: Cpp 3 | 4 | # Style 5 | BasedOnStyle: Microsoft 6 | 7 | # No indent for extern block, namespace, and access specifiers in class 8 | IndentExternBlock: NoIndent 9 | NamespaceIndentation: None 10 | AccessModifierOffset: -4 11 | 12 | # Header files order 13 | IncludeIsMainRegex: ".*" # 1. main file 14 | IncludeCategories: 15 | - Regex: '^' 16 | Priority: 1 17 | SortPriority: 0 18 | - Regex: '^<.*>' 19 | Priority: 2 20 | SortPriority: 0 21 | - Regex: '.*' 22 | Priority: 3 23 | SortPriority: 0 24 | 25 | # Indentation 26 | IndentWidth: 4 27 | 28 | # Line length 29 | ColumnLimit: 100 30 | 31 | # Break before braces 32 | BreakBeforeBraces: Custom 33 | BraceWrapping: 34 | AfterCaseLabel: true 35 | AfterControlStatement: true 36 | AfterFunction: true 37 | AfterNamespace: true 38 | 39 | 40 | KeepEmptyLinesAtTheStartOfBlocks: false 41 | -------------------------------------------------------------------------------- /lib/algorithm/frequency.cpp: -------------------------------------------------------------------------------- 1 | #include "algorithm/frequency.h" 2 | #include 3 | 4 | FrequencyPeak::FrequencyPeak(std::uint32_t fft_pass_number, std::uint32_t peak_magnitude, 5 | std::uint32_t corrected_peak_frequency_bin, std::uint32_t sample_rate) 6 | : fft_pass_number_(fft_pass_number), peak_magnitude_(peak_magnitude), 7 | corrected_peak_frequency_bin_(corrected_peak_frequency_bin), sample_rate_(sample_rate) 8 | { 9 | } 10 | 11 | FrequencyPeak::~FrequencyPeak() 12 | { 13 | } 14 | 15 | double FrequencyPeak::ComputeFrequency() const 16 | { 17 | return corrected_peak_frequency_bin_ * (static_cast(sample_rate_) / 2. / 1024. / 64.); 18 | } 19 | 20 | double FrequencyPeak::ComputeAmplitudePCM() const 21 | { 22 | return std::sqrt(std::exp((peak_magnitude_ - 6144) / 1477.3) * (1 << 17) / 2.) / 1024.; 23 | } 24 | 25 | double FrequencyPeak::ComputeElapsedSeconds() const 26 | { 27 | return static_cast(fft_pass_number_) * 128. / static_cast(sample_rate_); 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/build-macos-amd64.yaml: -------------------------------------------------------------------------------- 1 | name: MacOS AMD64 2 | 3 | on: 4 | push: 5 | branches: [main, develop] 6 | pull_request: 7 | branches: [main, develop] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | macos-amd64: 12 | runs-on: macos-13 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v4 16 | 17 | - name: Install dependencies 18 | run: brew install fftw --formula 19 | 20 | - name: Configure CMake 21 | run: cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SUFFIX=macos-amd64 22 | 23 | - name: Build 24 | run: cmake --build ${{ github.workspace }}/build --config Release 25 | 26 | - name: Check build results 27 | run: | 28 | file build/cli/vibra* 29 | file build/lib/libvibra* 30 | 31 | - name: Upload artifacts 32 | uses: actions/upload-artifact@v4 33 | with: 34 | name: vibra-macos-amd64 35 | path: | 36 | build/cli/vibra* 37 | build/lib/libvibra* 38 | -------------------------------------------------------------------------------- /.github/workflows/build-macos-arm64.yaml: -------------------------------------------------------------------------------- 1 | name: MacOS ARM64 2 | 3 | on: 4 | push: 5 | branches: [main, develop] 6 | pull_request: 7 | branches: [main, develop] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | macos-arm64: 12 | runs-on: macos-14 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v4 16 | 17 | - name: Install dependencies 18 | run: brew install fftw --formula 19 | 20 | - name: Configure CMake 21 | run: cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SUFFIX=macos-arm64 22 | 23 | - name: Build 24 | run: cmake --build ${{ github.workspace }}/build --config Release 25 | 26 | - name: Check build results 27 | run: | 28 | file build/cli/vibra* 29 | file build/lib/libvibra* 30 | 31 | - name: Upload artifacts 32 | uses: actions/upload-artifact@v4 33 | with: 34 | name: vibra-macos-arm64 35 | path: | 36 | build/cli/vibra* 37 | build/lib/libvibra* 38 | -------------------------------------------------------------------------------- /.github/workflows/build-linux-amd64.yaml: -------------------------------------------------------------------------------- 1 | name: Linux AMD64 2 | 3 | on: 4 | push: 5 | branches: [main, develop] 6 | pull_request: 7 | branches: [main, develop] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | linux-amd64: 12 | runs-on: ubuntu-22.04 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v4 16 | 17 | - name: Install dependencies 18 | run: | 19 | sudo apt-get update 20 | sudo apt-get install -y build-essential libfftw3-dev libcurl4-openssl-dev 21 | 22 | - name: Configure CMake 23 | run: cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SUFFIX=linux-amd64 24 | 25 | - name: Build 26 | run: cmake --build ${{ github.workspace }}/build --config Release 27 | 28 | - name: Check build results 29 | run: | 30 | file build/cli/vibra* 31 | file build/lib/libvibra* 32 | 33 | - name: Upload artifacts 34 | uses: actions/upload-artifact@v4 35 | with: 36 | name: vibra-linux-amd64 37 | path: | 38 | build/cli/vibra* 39 | build/lib/libvibra* 40 | -------------------------------------------------------------------------------- /cli/utils/uuid4.h: -------------------------------------------------------------------------------- 1 | #ifndef CLI_UTILS_UUID4_H_ 2 | #define CLI_UTILS_UUID4_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace uuid4 10 | { 11 | std::string generate() 12 | { 13 | std::random_device rd; 14 | std::mt19937 gen(rd()); 15 | std::uniform_int_distribution<> dis(0, 15); 16 | std::uniform_int_distribution<> dis2(8, 11); 17 | 18 | std::stringstream ss; 19 | ss << std::hex; 20 | for (int i = 0; i < 8; i++) 21 | { 22 | ss << dis(gen); 23 | } 24 | ss << "-"; 25 | for (int i = 0; i < 4; i++) 26 | { 27 | ss << dis(gen); 28 | } 29 | ss << "-4"; 30 | for (int i = 0; i < 3; i++) 31 | { 32 | ss << dis(gen); 33 | } 34 | ss << "-"; 35 | ss << dis2(gen); 36 | for (int i = 0; i < 3; i++) 37 | { 38 | ss << dis(gen); 39 | } 40 | ss << "-"; 41 | for (int i = 0; i < 12; i++) 42 | { 43 | ss << dis(gen); 44 | } 45 | 46 | return ss.str(); // RVO. Guaranteed copy elision since C++17 47 | } 48 | } // namespace uuid4 49 | 50 | #endif // CLI_UTILS_UUID4_H_ 51 | -------------------------------------------------------------------------------- /.github/workflows/build-linux-arm64.yaml: -------------------------------------------------------------------------------- 1 | name: Linux ARM64 2 | 3 | on: 4 | push: 5 | branches: [main, develop] 6 | pull_request: 7 | branches: [main, develop] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | linux-arm64: 12 | runs-on: ubuntu-22.04-arm 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v4 16 | 17 | - name: Install dependencies 18 | run: | 19 | sudo apt-get update 20 | sudo apt-get install -y build-essential libfftw3-dev libcurl4-openssl-dev 21 | 22 | - name: Configure CMake 23 | run: cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SUFFIX=linux-arm64 24 | 25 | - name: Build 26 | run: cmake --build ${{ github.workspace }}/build --config Release 27 | 28 | - name: Check build results 29 | run: | 30 | file build/cli/vibra* 31 | file build/lib/libvibra* 32 | 33 | - name: Upload artifacts 34 | uses: actions/upload-artifact@v4 35 | with: 36 | name: vibra-linux-arm64 37 | path: | 38 | build/cli/vibra* 39 | build/lib/libvibra* 40 | -------------------------------------------------------------------------------- /.github/workflows/build-windows-amd64.yaml: -------------------------------------------------------------------------------- 1 | name: Windows AMD64 2 | 3 | on: 4 | push: 5 | branches: [main, develop] 6 | pull_request: 7 | branches: [main, develop] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | windows-amd64: 12 | runs-on: windows-2022 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v4 16 | 17 | - name: Install dependencies 18 | run: vcpkg install curl:x64-windows fftw3:x64-windows 19 | 20 | - name: Configure CMake with vcpkg 21 | run: cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SUFFIX=windows-amd64 -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT\scripts\buildsystems\vcpkg.cmake" 22 | 23 | - name: Build 24 | run: cmake --build ${{ github.workspace }}/build --config Release 25 | 26 | - name: Check build results 27 | run: | 28 | Get-ChildItem build/cli/Release 29 | Get-ChildItem build/lib/Release 30 | 31 | - name: Upload artifacts 32 | uses: actions/upload-artifact@v4 33 | with: 34 | name: vibra-windows-amd64 35 | path: | 36 | build/cli/Release 37 | build/lib/Release -------------------------------------------------------------------------------- /bindings/wasm/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for building a WebAssembly module using Emscripten 2 | 3 | EMSDK_PATH := $(EMSDK) 4 | EMCC := $(EMSDK_PATH)/upstream/emscripten/emcc 5 | EMCXX := $(EMSDK_PATH)/upstream/emscripten/em++ 6 | 7 | SRC_DIR := . 8 | BUILD_DIR := build 9 | 10 | SOURCES := $(SRC_DIR)/wasm.cpp 11 | 12 | OBJECTS := $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%.o,$(SOURCES)) 13 | TARGET_JS := $(BUILD_DIR)/vibra.js 14 | 15 | WASM_SETTINGS := -s EXPORTED_FUNCTIONS="[ \ 16 | '_GetWavSignature', \ 17 | '_GetSignedPcmSignature', \ 18 | '_GetFloatPcmSignature', \ 19 | '_GetFingerprint', \ 20 | '_GetSampleMs', \ 21 | '_malloc', \ 22 | '_free' \ 23 | ]" \ 24 | -s EXPORTED_RUNTIME_METHODS="['ccall', 'cwrap']" \ 25 | -s ALLOW_MEMORY_GROWTH=1 \ 26 | -s WASM=1 27 | 28 | CFLAGS := -Os 29 | CXXFLAGS := -Os -std=c++11 -I/usr/local/include 30 | LDFLAGS := $(WASM_SETTINGS) -L/usr/local/lib -lvibra -lfftw3 31 | 32 | all: $(TARGET_JS) 33 | 34 | $(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp 35 | $(EMCXX) $(CXXFLAGS) -c $< -o $@ 36 | 37 | $(TARGET_JS): $(OBJECTS) 38 | $(EMCXX) $(OBJECTS) $(LDFLAGS) -o $@ 39 | 40 | clean: 41 | rm -rf $(BUILD_DIR)/* 42 | 43 | .PHONY: all clean -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(vibra) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | set(CMAKE_CXX_STANDARD_REQUIRED True) 6 | 7 | if(MSVC) 8 | # TODO: fix warnings 9 | # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /WX") 10 | else() 11 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -Wextra -pedantic -fno-strict-aliasing") 12 | endif() 13 | include_directories(${CMAKE_SOURCE_DIR}/lib) 14 | 15 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 16 | if(MSVC) 17 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Od /Zi") 18 | else() 19 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g") 20 | endif() 21 | else() 22 | if(MSVC) 23 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /O2") 24 | else() 25 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2") 26 | endif() 27 | endif() 28 | 29 | # build verbose output 30 | # set(CMAKE_VERBOSE_MAKEFILE ON) 31 | 32 | # enable profiling 33 | option(ENABLE_PROFILING "Enable profiling with -pg flag" OFF) 34 | if(ENABLE_PROFILING) 35 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pg") 36 | endif() 37 | 38 | add_subdirectory(lib) 39 | 40 | option(LIBRARY_ONLY "Build only the library" OFF) 41 | if(NOT LIBRARY_ONLY) 42 | add_subdirectory(cli) 43 | endif() -------------------------------------------------------------------------------- /lib/algorithm/frequency.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_ALGORITHM_FREQUENCY_H_ 2 | #define LIB_ALGORITHM_FREQUENCY_H_ 3 | 4 | #include 5 | 6 | enum class FrequencyBand 7 | { 8 | _0_150 = -1, 9 | _250_520, 10 | _520_1450, 11 | _1450_3500, 12 | _3500_5500, 13 | }; 14 | 15 | class FrequencyPeak 16 | { 17 | public: 18 | FrequencyPeak(std::uint32_t fft_pass_number, std::uint32_t peak_magnitude, 19 | std::uint32_t corrected_peak_frequency_bin, std::uint32_t sample_rate); 20 | ~FrequencyPeak(); 21 | 22 | inline std::uint32_t fft_pass_number() const 23 | { 24 | return fft_pass_number_; 25 | } 26 | inline std::uint32_t peak_magnitude() const 27 | { 28 | return peak_magnitude_; 29 | } 30 | inline std::uint32_t corrected_peak_frequency_bin() const 31 | { 32 | return corrected_peak_frequency_bin_; 33 | } 34 | inline double ComputeFrequency() const; 35 | inline double ComputeAmplitudePCM() const; 36 | inline double ComputeElapsedSeconds() const; 37 | 38 | private: 39 | std::uint32_t fft_pass_number_; 40 | std::uint32_t peak_magnitude_; 41 | std::uint32_t corrected_peak_frequency_bin_; 42 | std::uint32_t sample_rate_; 43 | }; 44 | 45 | #endif // LIB_ALGORITHM_FREQUENCY_H_ 46 | -------------------------------------------------------------------------------- /cli/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Fetch the args header-only library 2 | include(FetchContent) 3 | FetchContent_Declare( 4 | args 5 | GIT_REPOSITORY https://github.com/Taywee/args.git 6 | GIT_TAG master 7 | ) 8 | FetchContent_Populate(args) 9 | 10 | # Add the executable for the CLI 11 | add_executable(vibra 12 | main.cpp 13 | cli.cpp 14 | communication/shazam.cpp 15 | ) 16 | 17 | # Add the args include directory to the target 18 | target_include_directories(vibra PRIVATE ${args_SOURCE_DIR}) 19 | 20 | # Add the include directory to the target 21 | target_include_directories(vibra PRIVATE ${CMAKE_SOURCE_DIR}/cli) 22 | 23 | # Link the CLI with the static libvibra and libcurl 24 | if(MSVC) 25 | find_package(CURL REQUIRED) 26 | target_link_libraries(vibra PRIVATE vibra_static CURL::libcurl) 27 | else() 28 | find_library(CURL_LIBRARY NAMES curl) 29 | target_link_libraries(vibra PRIVATE vibra_static ${CURL_LIBRARY}) 30 | endif() 31 | 32 | # Set the output name 33 | if (DEFINED BUILD_SUFFIX) 34 | message(STATUS "Adding suffix: ${BUILD_SUFFIX}") 35 | set(SUFFIX "-${BUILD_SUFFIX}") 36 | else() 37 | set(SUFFIX "") 38 | endif() 39 | set_target_properties(vibra PROPERTIES OUTPUT_NAME "vibra${SUFFIX}") 40 | 41 | # Install the CLI 42 | install(TARGETS vibra 43 | RUNTIME DESTINATION bin 44 | ) -------------------------------------------------------------------------------- /bindings/wasm/src/index.ts: -------------------------------------------------------------------------------- 1 | import EventEmitter from 'events'; 2 | import Module from './vibra' 3 | 4 | export class Singature { 5 | uri: string; 6 | samplems: number; 7 | 8 | constructor(uri: string, samplems: number) { 9 | this.uri = uri; 10 | this.samplems = samplems; 11 | } 12 | } 13 | 14 | export class Vibra extends EventEmitter { 15 | public initialized = false; 16 | 17 | constructor() { 18 | super(); 19 | Module.onRuntimeInitialized = () => { 20 | this.initialized = true; 21 | this.emit('initialized'); 22 | }; 23 | } 24 | 25 | getSignature(rawwav: Uint8Array): Singature { 26 | if (!this.initialized) { 27 | throw new Error('Vibra not initialized'); 28 | } 29 | 30 | const dataPtr = Module._malloc(rawwav.length); 31 | Module.HEAPU8.set(rawwav, dataPtr); 32 | const signaturePtr = Module.ccall( 33 | 'GetWavSignature', 34 | 'number', 35 | ['number', 'number'], 36 | [dataPtr, rawwav.length] 37 | ); 38 | Module._free(dataPtr); 39 | 40 | const uri = Module.ccall('GetFingerprint', 'string', ['number'], [signaturePtr]); 41 | const samplems = Module.ccall('GetSampleMs', 'number', ['number'], [signaturePtr]); 42 | 43 | Module.ccall('FreeFingerprint', 'void', ['number'], [signaturePtr]); 44 | 45 | return new Singature(uri, samplems); 46 | } 47 | } -------------------------------------------------------------------------------- /bindings/wasm/src/shazam.ts: -------------------------------------------------------------------------------- 1 | import { Singature } from './index'; 2 | import { USER_AGENTS } from './uger_agent'; 3 | import { v4 as UUID } from 'uuid'; 4 | import axios from 'axios'; 5 | 6 | export class Shazam { 7 | static endpoint: string = "https://amp.shazam.com/discovery/v5/fr/FR/android/-/tag/"; 8 | 9 | static async recognize(signature: Singature) { 10 | let uuid1: string = UUID().toUpperCase(); 11 | let uuid2: string = UUID().toUpperCase(); 12 | 13 | let url: string = `${Shazam.endpoint}${uuid1}/${uuid2}`; 14 | url += "?sync=true&webv3=true&sampling=true&connected=&shazamapiversion=v3&sharehub=true&video=v3"; 15 | 16 | let body = { 17 | geolocation: { 18 | altitude: 300, 19 | latitude: 45, 20 | longitude: 2 21 | }, 22 | signature: { 23 | uri: signature.uri, 24 | samplems: signature.samplems, 25 | timestamp: Date.now() 26 | }, 27 | timestamp: Date.now(), 28 | timezone: "Europe/Paris" 29 | }; 30 | 31 | let headers = { 32 | "Content-Type": "application/json", 33 | "User-Agent": USER_AGENTS[Math.floor(Math.random() * USER_AGENTS.length)], 34 | "Content-Language": "en_US" 35 | }; 36 | 37 | let response = await axios.post(url, body, { headers: headers }); 38 | return response.data; 39 | } 40 | } -------------------------------------------------------------------------------- /lib/algorithm/signature_generator.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_ALGORITHM_SIGNATURE_GENERATOR_H_ 2 | #define LIB_ALGORITHM_SIGNATURE_GENERATOR_H_ 3 | 4 | #include "algorithm/signature.h" 5 | #include "audio/downsampler.h" 6 | #include "utils/fft.h" 7 | #include "utils/ring_buffer.h" 8 | 9 | constexpr std::size_t MAX_PEAKS = 255u; 10 | constexpr std::size_t FFT_BUFFER_CHUNK_SIZE = 2048u; 11 | 12 | class SignatureGenerator 13 | { 14 | public: 15 | SignatureGenerator(); 16 | void FeedInput(const LowQualityTrack &input); 17 | Signature GetNextSignature(); 18 | 19 | inline void AddSampleProcessed(std::uint32_t sample_processed) 20 | { 21 | sample_processed_ += sample_processed; 22 | } 23 | 24 | inline void set_max_time_seconds(double max_time_seconds) 25 | { 26 | max_time_seconds_ = max_time_seconds; 27 | } 28 | 29 | private: 30 | void processInput(const LowQualityTrack &input); 31 | void doFFT(const LowQualityTrack &input); 32 | void doPeakSpreadingAndRecoginzation(); 33 | void doPeakSpreading(); 34 | void doPeakRecognition(); 35 | void resetSignatureGenerater(); 36 | 37 | private: 38 | LowQualityTrack input_pending_processing_; 39 | std::uint32_t sample_processed_; 40 | double max_time_seconds_; 41 | 42 | fft::FFT fft_object_; 43 | Signature next_signature_; 44 | RingBuffer samples_ring_buffer_; 45 | RingBuffer fft_outputs_; 46 | RingBuffer spread_ffts_output_; 47 | }; 48 | 49 | #endif // LIB_ALGORITHM_SIGNATURE_GENERATOR_H_ 50 | -------------------------------------------------------------------------------- /lib/audio/byte_control.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_AUDIO_BYTE_CONTROL_H_ 2 | #define LIB_AUDIO_BYTE_CONTROL_H_ 3 | 4 | #include 5 | 6 | #define GETINTX(T, cp, i) (*(T *)((unsigned char *)(cp) + (i))) // NOLINT [readability-casting] 7 | #define GETINT8(cp, i) GETINTX(std::int8_t, (cp), (i)) 8 | #define GETINT16(cp, i) GETINTX(std::int16_t, (cp), (i)) 9 | #define GETINT32(cp, i) GETINTX(std::int32_t, (cp), (i)) 10 | #define GETINT64(cp, i) GETINTX(std::int64_t, (cp), (i)) 11 | 12 | #ifdef WORDS_BIGENDIAN 13 | #define GETINT24(cp, i) \ 14 | (((unsigned char *)(cp) + (i))[2] + (((unsigned char *)(cp) + (i))[1] * (1 << 8)) + \ 15 | (((signed char *)(cp) + (i))[0] * (1 << 16))) 16 | #else 17 | #define GETINT24(cp, i) \ 18 | (((unsigned char *)(cp) + (i))[0] + (((unsigned char *)(cp) + (i))[1] * (1 << 8)) + \ 19 | (((signed char *)(cp) + (i))[2] * (1 << 16))) 20 | #endif 21 | 22 | #define GETSAMPLE64(size, cp, i) \ 23 | (((size) == 1) ? (std::int64_t)GETINT8((cp), (i)) * (1LL << 56) \ 24 | : ((size) == 2) ? (std::int64_t)GETINT16((cp), (i)) * (1LL << 48) \ 25 | : ((size) == 3) ? (std::int64_t)GETINT24((cp), (i)) * (1LL << 40) \ 26 | : ((size) == 4) ? (std::int64_t)GETINT32((cp), (i)) * (1LL << 32) \ 27 | : (std::int64_t)GETINT64((cp), (i))) 28 | 29 | #endif // LIB_AUDIO_BYTE_CONTROL_H_ 30 | -------------------------------------------------------------------------------- /cli/communication/timezones.h: -------------------------------------------------------------------------------- 1 | #ifndef CLI_COMMUNICATION_TIMEZONES_H_ 2 | #define CLI_COMMUNICATION_TIMEZONES_H_ 3 | 4 | static constexpr const char *EUROPE_TIMEZONES[] = { 5 | "Europe/Amsterdam", "Europe/Andorra", "Europe/Astrakhan", "Europe/Athens", 6 | "Europe/Belgrade", "Europe/Berlin", "Europe/Bratislava", "Europe/Brussels", 7 | "Europe/Bucharest", "Europe/Budapest", "Europe/Busingen", "Europe/Chisinau", 8 | "Europe/Copenhagen", "Europe/Dublin", "Europe/Gibraltar", "Europe/Guernsey", 9 | "Europe/Helsinki", "Europe/Isle_of_Man", "Europe/Istanbul", "Europe/Jersey", 10 | "Europe/Kaliningrad", "Europe/Kiev", "Europe/Kirov", "Europe/Lisbon", 11 | "Europe/Ljubljana", "Europe/London", "Europe/Luxembourg", "Europe/Madrid", 12 | "Europe/Malta", "Europe/Mariehamn", "Europe/Minsk", "Europe/Monaco", 13 | "Europe/Moscow", "Europe/Oslo", "Europe/Paris", "Europe/Podgorica", 14 | "Europe/Prague", "Europe/Riga", "Europe/Rome", "Europe/Samara", 15 | "Europe/San_Marino", "Europe/Sarajevo", "Europe/Saratov", "Europe/Simferopol", 16 | "Europe/Skopje", "Europe/Sofia", "Europe/Stockholm", "Europe/Tallinn", 17 | "Europe/Tirane", "Europe/Ulyanovsk", "Europe/Uzhgorod", "Europe/Vaduz", 18 | "Europe/Vatican", "Europe/Vienna", "Europe/Vilnius", "Europe/Volgograd", 19 | "Europe/Warsaw", "Europe/Zagreb", "Europe/Zaporozhye", "Europe/Zurich"}; 20 | 21 | constexpr unsigned int EUROPE_TIMEZONES_SIZE = 22 | sizeof(EUROPE_TIMEZONES) / sizeof(EUROPE_TIMEZONES[0]); 23 | 24 | #endif // CLI_COMMUNICATION_TIMEZONES_H_ 25 | -------------------------------------------------------------------------------- /bindings/wasm/wasm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifdef __cplusplus 5 | extern "C" 6 | { 7 | #endif 8 | 9 | Fingerprint *EMSCRIPTEN_KEEPALIVE GetWavSignature(char *raw_wav, int wav_data_size) 10 | { 11 | return vibra_get_fingerprint_from_wav_data(raw_wav, wav_data_size); 12 | } 13 | 14 | Fingerprint *EMSCRIPTEN_KEEPALIVE GetSignedPcmSignature(char *raw_pcm, int pcm_data_size, 15 | int sample_rate, int sample_width, 16 | int channel_count) 17 | { 18 | return vibra_get_fingerprint_from_signed_pcm(raw_pcm, pcm_data_size, sample_rate, sample_width, 19 | channel_count); 20 | } 21 | 22 | Fingerprint *EMSCRIPTEN_KEEPALIVE GetFloatPcmSignature(char *raw_pcm, int pcm_data_size, 23 | int sample_rate, int sample_width, 24 | int channel_count) 25 | { 26 | return vibra_get_fingerprint_from_float_pcm(raw_pcm, pcm_data_size, sample_rate, sample_width, 27 | channel_count); 28 | } 29 | 30 | const char *EMSCRIPTEN_KEEPALIVE GetFingerprint(Fingerprint *signature) 31 | { 32 | return vibra_get_uri_from_fingerprint(signature); 33 | } 34 | 35 | unsigned int EMSCRIPTEN_KEEPALIVE GetSampleMs(Fingerprint *signature) 36 | { 37 | return vibra_get_sample_ms_from_fingerprint(signature); 38 | } 39 | 40 | void EMSCRIPTEN_KEEPALIVE FreeFingerprint(Fingerprint *signature) 41 | { 42 | vibra_free_fingerprint(signature); 43 | } 44 | 45 | #ifdef __cplusplus 46 | } 47 | #endif 48 | -------------------------------------------------------------------------------- /lib/utils/base64.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_UTILS_BASE64_H_ 2 | #define LIB_UTILS_BASE64_H_ 3 | 4 | #include 5 | 6 | namespace base64 7 | { 8 | static const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 9 | "abcdefghijklmnopqrstuvwxyz" 10 | "0123456789+/"; 11 | std::string encode(const char *bytes_to_encode, unsigned int in_len) 12 | { 13 | std::string ret; 14 | int i = 0; 15 | int j = 0; 16 | unsigned char char_array_3[3]; 17 | unsigned char char_array_4[4]; 18 | 19 | while (in_len--) 20 | { 21 | char_array_3[i++] = *(bytes_to_encode++); 22 | if (i == 3) 23 | { 24 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 25 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 26 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 27 | char_array_4[3] = char_array_3[2] & 0x3f; 28 | 29 | for (i = 0; (i < 4); i++) 30 | ret += base64_chars[char_array_4[i]]; 31 | i = 0; 32 | } 33 | } 34 | 35 | if (i) 36 | { 37 | for (j = i; j < 3; j++) 38 | char_array_3[j] = '\0'; 39 | 40 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 41 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 42 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 43 | 44 | for (j = 0; (j < i + 1); j++) 45 | ret += base64_chars[char_array_4[j]]; 46 | 47 | while ((i++ < 3)) 48 | ret += '='; 49 | } 50 | 51 | return ret; 52 | } 53 | } // namespace base64 54 | 55 | #endif // LIB_UTILS_BASE64_H_ 56 | -------------------------------------------------------------------------------- /.github/workflows/build-webassembly.yaml: -------------------------------------------------------------------------------- 1 | name: WebAssembly 2 | 3 | on: 4 | push: 5 | branches: [main, develop] 6 | pull_request: 7 | branches: [main, develop] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | web-assembly: 12 | runs-on: ubuntu-22.04 13 | steps: 14 | - name: Setup emsdk 15 | uses: mymindstorm/setup-emsdk@v14 16 | 17 | - name: Checkout repository 18 | uses: actions/checkout@v4 19 | 20 | - name: Build FFTW3 for WebAssembly 21 | run: | 22 | emsdk install latest 23 | emsdk activate latest 24 | wget http://www.fftw.org/fftw-3.3.10.tar.gz 25 | tar -xvf fftw-3.3.10.tar.gz 26 | cd fftw-3.3.10 27 | emconfigure ./configure \ 28 | --disable-fortran \ 29 | --prefix=/usr/local \ 30 | --disable-dependency-tracking 31 | emmake make -j9 32 | sudo make install 33 | 34 | - name: Configure CMake 35 | run: > 36 | emcmake cmake -B ${{ github.workspace }}/build \ 37 | -DFFTW3_PATH=/usr/local \ 38 | -DLIBRARY_ONLY=ON \ 39 | -DCMAKE_INSTALL_PREFIX=/usr/local 40 | 41 | - name: Build libvibra as WebAssembly Library 42 | run: | 43 | cd ${{ github.workspace }}/build 44 | make 45 | sudo make install 46 | 47 | - name: Build vibra WebAssembly Module 48 | run: | 49 | cd ${{ github.workspace }}/bindings/wasm 50 | mkdir build 51 | emmake make -j8 52 | 53 | - name: Upload artifacts 54 | uses: actions/upload-artifact@v4 55 | with: 56 | name: vibra-web-assembly 57 | path: | 58 | bindings/wasm/build/vibra.wasm 59 | bindings/wasm/build/vibra.js 60 | -------------------------------------------------------------------------------- /lib/utils/ring_buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_UTILS_RING_BUFFER_H_ 2 | #define LIB_UTILS_RING_BUFFER_H_ 3 | 4 | #include 5 | 6 | template class RingBuffer : private std::vector 7 | { 8 | public: 9 | explicit RingBuffer(std::size_t size, T &&default_value = T()); 10 | virtual ~RingBuffer(); 11 | 12 | void Append(const T &value); 13 | std::uint32_t size() const 14 | { 15 | return std::vector::size(); 16 | } 17 | std::uint32_t &num_written() 18 | { 19 | return num_written_; 20 | } 21 | std::uint32_t &position() 22 | { 23 | return position_; 24 | } 25 | 26 | T &operator[](std::int32_t index); 27 | 28 | typename std::vector::iterator begin() 29 | { 30 | return std::vector::begin(); 31 | } 32 | typename std::vector::iterator end() 33 | { 34 | return std::vector::end(); 35 | } 36 | 37 | private: 38 | std::uint32_t num_written_; 39 | std::uint32_t position_; 40 | }; 41 | 42 | template T &RingBuffer::operator[](std::int32_t index) 43 | { 44 | if (index < 0) 45 | { 46 | index = std::vector::size() + index; 47 | // support negative index 48 | } 49 | return std::vector::operator[](index); 50 | } 51 | 52 | template 53 | RingBuffer::RingBuffer(std::size_t size, T &&default_value) 54 | : std::vector(size, default_value), num_written_(0), position_(0) 55 | { 56 | } 57 | 58 | template RingBuffer::~RingBuffer() 59 | { 60 | } 61 | 62 | template void RingBuffer::Append(const T &value) 63 | { 64 | this->operator[](position_) = value; 65 | position_ = (position_ + 1) % std::vector::size(); 66 | num_written_++; 67 | } 68 | 69 | #endif // LIB_UTILS_RING_BUFFER_H_ 70 | -------------------------------------------------------------------------------- /bindings/wasm/README.md: -------------------------------------------------------------------------------- 1 |

2 | WebAssembly Logo 3 |

4 | 5 | ### 1. Building the WebAssembly Version of Vibra Using Docker 6 | 7 | Follow these steps to build the WebAssembly version of Vibra: 8 | 9 | 1. Ensure Docker is installed on your system. If not, visit the official Docker website for installation instructions specific to your operating system. 10 | 11 | 2. Clone the Vibra repository: 12 | ```bash 13 | git clone https://github.com/bayernmuller/vibra 14 | ``` 15 | 16 | 3. Navigate to the Vibra directory: 17 | ```bash 18 | cd vibra 19 | ``` 20 | 21 | 4. Build the Docker image: 22 | ```bash 23 | docker build -f bindings/wasm/Dockerfile -t vibra-wasm . 24 | ``` 25 | 26 | 5. Run the Docker container to compile the WebAssembly files: 27 | ```bash 28 | docker run --rm -v $(pwd):/app vibra-wasm bash -c "cd bindings/wasm && ./build-wasm.sh" 29 | ``` 30 | 31 | 6. After compilation, you'll find the following files in the `wasm/build` directory: 32 | ```bash 33 | $ ls -l bindings/wasm/build 34 | .rw-r--r-- jayden staff 88 KB Wed Sep 11 23:07:52 2024 vibra.js 35 | .rwxr-xr-x jayden staff 804 KB Wed Sep 11 23:07:52 2024 vibra.wasm 36 | ``` 37 | 38 | ##### If you can't build the WebAssembly version, you can download `vibra.wasm` and `vibra.js` from GitHub workflow artifacts. 39 | * https://github.com/BayernMuller/vibra/actions/workflows/build-webassembly.yaml 40 | * Please download `vibra.wasm` and `vibra.js` from the latest successful run. 41 | * and put them in the `vibra/bindings/wasm/build` directory to run the demo Node.js app. 42 | 43 | ### 2. Running the Demo Node.js App "vibrajs" 44 | 45 | To run the demo Node.js application: 46 | 47 | 1. Install dependencies and build the CLI app: 48 | ```bash 49 | cd bindings/wasm 50 | npm install 51 | npm run build 52 | npm link 53 | ``` 54 | 55 | 2. Use the CLI app to recognize a song: 56 | ```bash 57 | $ vibrajs "stairway_to_heaven.wav" | jq .track.subtitle 58 | "Led Zeppelin" 59 | ``` 60 | 61 | This example demonstrates how to use Vibrajs to identify the artist of the song "Stairway to Heaven". -------------------------------------------------------------------------------- /lib/algorithm/signature.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_ALGORITHM_SIGNATURE_H_ 2 | #define LIB_ALGORITHM_SIGNATURE_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "algorithm/frequency.h" 10 | 11 | // Prevent Structure Padding 12 | #ifdef _MSC_VER 13 | #pragma pack(push, 1) 14 | #define PACKED_ATTRIBUTE 15 | #else 16 | #define PACKED_ATTRIBUTE __attribute__((packed)) 17 | #endif 18 | 19 | struct RawSignatureHeader 20 | { 21 | uint32_t magic1; 22 | uint32_t crc32; 23 | uint32_t size_minus_header; 24 | uint32_t magic2; 25 | uint32_t void1[3]; 26 | uint32_t shifted_sample_rate_id; 27 | uint32_t void2[2]; 28 | uint32_t number_samples_plus_divided_sample_rate; 29 | uint32_t fixed_value; 30 | } PACKED_ATTRIBUTE; 31 | 32 | #ifdef _MSC_VER 33 | #pragma pack(pop) 34 | #endif 35 | 36 | class Signature 37 | { 38 | public: 39 | Signature(std::uint32_t sample_rate, std::uint32_t num_samples); 40 | ~Signature(); 41 | void Reset(std::uint32_t sampleRate, std::uint32_t num_samples); 42 | 43 | inline void Addnum_samples(std::uint32_t num_samples) 44 | { 45 | num_samples_ += num_samples; 46 | } 47 | inline std::uint32_t sample_rate() const 48 | { 49 | return sample_rate_; 50 | } 51 | inline std::uint32_t num_samples() const 52 | { 53 | return num_samples_; 54 | } 55 | inline std::map> &frequency_band_to_peaks() 56 | { 57 | return frequency_band_to_peaks_; 58 | } 59 | std::uint32_t SumOfPeaksLength() const; 60 | std::string EncodeBase64() const; 61 | 62 | private: 63 | template 64 | std::stringstream &write_little_endian(std::stringstream &stream, const T &&value, 65 | size_t size = sizeof(T)) const 66 | { 67 | for (size_t i = 0; i < size; ++i) 68 | { 69 | stream << static_cast(value >> (i << 3)); 70 | } 71 | return stream; 72 | } 73 | 74 | private: 75 | std::uint32_t sample_rate_; 76 | std::uint32_t num_samples_; 77 | std::map> frequency_band_to_peaks_; 78 | }; 79 | 80 | #endif // LIB_ALGORITHM_SIGNATURE_H_ 81 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Define the sources for the library 2 | set(LIBVIBRA_SOURCES 3 | vibra.cpp 4 | algorithm/signature.cpp 5 | algorithm/frequency.cpp 6 | algorithm/signature_generator.cpp 7 | audio/wav.cpp 8 | audio/downsampler.cpp 9 | ) 10 | 11 | # Add shared and static libraries for libvibra 12 | add_library(vibra_shared SHARED ${LIBVIBRA_SOURCES}) 13 | add_library(vibra_static STATIC ${LIBVIBRA_SOURCES}) 14 | 15 | target_include_directories(vibra_shared PRIVATE ${CMAKE_SOURCE_DIR}/include) 16 | target_include_directories(vibra_static PRIVATE ${CMAKE_SOURCE_DIR}/include) 17 | 18 | if (DEFINED FFTW3_PATH) 19 | set(FFTW3_INCLUDE_DIR ${FFTW3_PATH}/include) 20 | set(FFTW3_LIBRARY ${FFTW3_PATH}/lib/libfftw3.a) 21 | else() 22 | find_path(FFTW3_INCLUDE_DIR fftw3.h) 23 | find_library(FFTW3_LIBRARY NAMES fftw3) 24 | endif() 25 | if (NOT FFTW3_LIBRARY OR NOT FFTW3_INCLUDE_DIR) 26 | message(FATAL_ERROR "FFTW3 library or include path not found. Please install FFTW3 or specify FFTW3_PATH.") 27 | endif() 28 | 29 | message(STATUS "FFTW3_INCLUDE_DIR: ${FFTW3_INCLUDE_DIR}") 30 | message(STATUS "FFTW3_LIBRARY: ${FFTW3_LIBRARY}") 31 | target_include_directories(vibra_shared PRIVATE ${FFTW3_INCLUDE_DIR}) 32 | target_include_directories(vibra_static PRIVATE ${FFTW3_INCLUDE_DIR}) 33 | target_link_libraries(vibra_shared PRIVATE ${FFTW3_LIBRARY}) 34 | target_link_libraries(vibra_static PRIVATE ${FFTW3_LIBRARY}) 35 | 36 | # Set C++11 standard 37 | set_target_properties(vibra_shared PROPERTIES CXX_STANDARD 11) 38 | set_target_properties(vibra_static PROPERTIES CXX_STANDARD 11) 39 | 40 | # Set output names 41 | if (DEFINED BUILD_SUFFIX) 42 | message(STATUS "Adding suffix: ${BUILD_SUFFIX}") 43 | set(SUFFIX "-${BUILD_SUFFIX}") 44 | else() 45 | set(SUFFIX "") 46 | endif() 47 | set_target_properties(vibra_shared PROPERTIES OUTPUT_NAME "vibra${SUFFIX}") 48 | set_target_properties(vibra_static PROPERTIES OUTPUT_NAME "vibra${SUFFIX}") 49 | 50 | # Install library files to standard system paths 51 | install(TARGETS vibra_shared vibra_static 52 | LIBRARY DESTINATION lib 53 | ARCHIVE DESTINATION lib 54 | ) 55 | 56 | # Install header files to the include directory 57 | install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ 58 | DESTINATION include 59 | FILES_MATCHING PATTERN "*.h" 60 | ) -------------------------------------------------------------------------------- /lib/utils/fft.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_UTILS_FFT_H_ 2 | #define LIB_UTILS_FFT_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include // NOLINT [include_order] 8 | #include 9 | #include 10 | 11 | namespace fft 12 | { 13 | 14 | template 15 | class FFT 16 | { 17 | public: 18 | constexpr static const int OUTPUT_SIZE = INPUT_SIZE / 2 + 1; 19 | using FFTOutput = std::array; 20 | 21 | public: 22 | FFT() 23 | : input_data_buffer_(fftw_alloc_real(INPUT_SIZE), fftw_free), 24 | output_data_buffer_(fftw_alloc_complex(OUTPUT_SIZE), fftw_free) 25 | { 26 | fftw_plan_ = fftw_plan_dft_r2c_1d(INPUT_SIZE, input_data_buffer_.get(), 27 | output_data_buffer_.get(), FFTW_ESTIMATE); 28 | } 29 | FFT(const FFT &) = delete; 30 | FFT &operator=(const FFT &) = delete; 31 | FFT(FFT &&) = delete; 32 | FFT &operator=(FFT &&) = delete; 33 | 34 | template FFTOutput RFFT(const Iterable &input) 35 | { 36 | assert(input.size() == INPUT_SIZE && 37 | "Input size must be equal to the input size specified in the constructor"); 38 | 39 | FFTOutput real_output; 40 | 41 | // Copy and convert the input data to double 42 | for (std::size_t i = 0; i < INPUT_SIZE; i++) 43 | { 44 | input_data_buffer_.get()[i] = static_cast(input[i]); 45 | } 46 | fftw_execute(fftw_plan_); 47 | 48 | double real_val = 0.0; 49 | double imag_val = 0.0; 50 | const double min_val = 1e-10; 51 | const double scale_factor = 1.0 / (1 << 17); 52 | 53 | // do max((real^2 + imag^2) / (1 << 17), 0.0000000001) 54 | for (std::size_t i = 0; i < OUTPUT_SIZE; ++i) 55 | { 56 | real_val = output_data_buffer_.get()[i][0]; 57 | imag_val = output_data_buffer_.get()[i][1]; 58 | 59 | real_val = (real_val * real_val + imag_val * imag_val) * scale_factor; 60 | real_output[i] = (real_val < min_val) ? min_val : real_val; 61 | } 62 | return real_output; 63 | } 64 | 65 | virtual ~FFT() 66 | { 67 | fftw_destroy_plan(fftw_plan_); 68 | fftw_cleanup(); 69 | } 70 | 71 | private: 72 | fftw_plan fftw_plan_; 73 | std::unique_ptr input_data_buffer_; 74 | std::unique_ptr output_data_buffer_; 75 | }; 76 | } // namespace fft 77 | 78 | #endif // LIB_UTILS_FFT_H_ 79 | -------------------------------------------------------------------------------- /lib/audio/downsampler.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_AUDIO_DOWNSAMPLER_H_ 2 | #define LIB_AUDIO_DOWNSAMPLER_H_ 3 | 4 | #include 5 | #include 6 | 7 | // forward declaration 8 | class Wav; 9 | // 10 | 11 | using LowQualitySample = std::int16_t; 12 | using LowQualityTrack = std::vector; 13 | 14 | constexpr std::uint32_t LOW_QUALITY_SAMPLE_RATE = 16000; 15 | constexpr std::uint32_t LOW_QUALITY_SAMPLE_BIT_WIDTH = sizeof(LowQualitySample) * 8; 16 | constexpr std::uint32_t LOW_QUALITY_SAMPLE_MAX = 32767; 17 | 18 | using DownsampleFunc = void (*)(LowQualityTrack *, const void *, double, std::uint32_t, 19 | std::uint32_t, std::uint32_t); 20 | 21 | class Downsampler 22 | { 23 | public: 24 | static LowQualityTrack GetLowQualityPCM(const Wav &wav, std::int32_t start_sec = 0, 25 | std::int32_t end_sec = -1); 26 | 27 | private: 28 | static DownsampleFunc getDownsampleFunc(bool is_signed, std::uint32_t width, 29 | std::uint32_t channels); 30 | 31 | static void signedStereoToMono(LowQualityTrack *dst, const void *src, double downsample_ratio, 32 | std::uint32_t new_sample_count, std::uint32_t width, 33 | std::uint32_t channels); 34 | static void signedMonoToMono(LowQualityTrack *dst, const void *src, double downsample_ratio, 35 | std::uint32_t new_sample_count, std::uint32_t width, 36 | std::uint32_t channels); 37 | static void signedMultiToMono(LowQualityTrack *dst, const void *src, double downsample_ratio, 38 | std::uint32_t new_sample_count, std::uint32_t width, 39 | std::uint32_t channels); 40 | 41 | template 42 | static void floatStereoToMono(LowQualityTrack *dst, const void *src, double downsample_ratio, 43 | std::uint32_t new_sample_count, std::uint32_t width, 44 | std::uint32_t channels); 45 | template 46 | static void floatMonoToMono(LowQualityTrack *dst, const void *src, double downsample_ratio, 47 | std::uint32_t new_sample_count, std::uint32_t width, 48 | std::uint32_t channels); 49 | template 50 | static void floatMultiToMono(LowQualityTrack *dst, const void *src, double downsample_ratio, 51 | std::uint32_t new_sample_count, std::uint32_t width, 52 | std::uint32_t channels); 53 | }; 54 | 55 | #endif // LIB_AUDIO_DOWNSAMPLER_H_ 56 | -------------------------------------------------------------------------------- /lib/audio/wav.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_AUDIO_WAV_H_ 2 | #define LIB_AUDIO_WAV_H_ 3 | 4 | #include 5 | #include 6 | #include "audio/byte_control.h" 7 | 8 | struct WavHeader 9 | { 10 | char riff_header[4]; // "RIFF" 11 | std::uint32_t file_size; 12 | char wave_header[4]; // "WAVE" 13 | }; 14 | 15 | struct FmtSubchunk 16 | { 17 | std::uint16_t audio_format; // 1 = PCM, 3 = IEEE float, etc. 18 | std::uint16_t num_channels; 19 | std::uint32_t sample_rate; 20 | std::uint32_t byte_rate; 21 | std::uint16_t block_align; 22 | std::uint16_t bits_per_sample; 23 | }; 24 | 25 | enum class AudioFormat 26 | { 27 | PCM_INTEGER = 1, 28 | PCM_FLOAT = 3, 29 | }; 30 | 31 | class Wav 32 | { 33 | public: 34 | Wav(Wav &&) = default; 35 | Wav(const Wav &) = delete; 36 | static Wav FromFile(const std::string &wav_file_path); 37 | static Wav FromRawWav(const char *raw_wav, std::uint32_t raw_wav_size); 38 | static Wav FromSignedPCM(const char *raw_pcm, std::uint32_t raw_pcm_size, 39 | std::uint32_t sample_rate, std::uint32_t sample_width, 40 | std::uint32_t channel_count); 41 | static Wav FromFloatPCM(const char *raw_pcm, std::uint32_t raw_pcm_size, 42 | std::uint32_t sample_rate, std::uint32_t sample_width, 43 | std::uint32_t channel_count); 44 | ~Wav(); 45 | 46 | inline std::uint16_t audio_format() const 47 | { 48 | return fmt_.audio_format; 49 | } 50 | inline std::uint16_t num_channels() const 51 | { 52 | return fmt_.num_channels; 53 | } 54 | inline std::uint32_t sample_rate_() const 55 | { 56 | return fmt_.sample_rate; 57 | } 58 | inline std::uint32_t bits_per_sample() const 59 | { 60 | return fmt_.bits_per_sample; 61 | } 62 | inline std::uint32_t data_size() const 63 | { 64 | return data_size_; 65 | } 66 | inline std::uint32_t file_size() const 67 | { 68 | return header_.file_size; 69 | } 70 | inline const std::unique_ptr &data() const 71 | { 72 | return data_; 73 | } 74 | 75 | private: 76 | Wav() = default; 77 | static Wav fromPCM(const char *raw_pcm, std::uint32_t raw_pcm_size, AudioFormat audio_format, 78 | std::uint32_t sample_rate, std::uint32_t sample_width, 79 | std::uint32_t channel_count); 80 | void readWavFileBuffer(std::istream &stream); 81 | 82 | private: 83 | WavHeader header_; 84 | FmtSubchunk fmt_; 85 | std::string wav_file_path_; 86 | std::uint32_t data_size_; 87 | std::unique_ptr data_; 88 | }; 89 | 90 | #endif // LIB_AUDIO_WAV_H_ 91 | -------------------------------------------------------------------------------- /tests/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | TEST_TARGET=sample.mp3 6 | TEST_TARGET_TITLE="Misty" 7 | VIBRA_CLI=vibra 8 | SHAZAM_REQUEST_DELAY=${1:-3} 9 | FAILED=0 10 | 11 | function pass() { 12 | printf "\e[32m%s\e[0m\n" "$1" 13 | } 14 | 15 | function fail() { 16 | printf "\e[31m%s\e[0m\n" "$1" 17 | FAILED=1 18 | } 19 | 20 | function info() { 21 | printf " - %s\t" "$1" 22 | } 23 | 24 | function recognize_audio() { 25 | local file=$1 26 | $VIBRA_CLI --recognize --file "$file" | jq .track.title -r 27 | sleep "$SHAZAM_REQUEST_DELAY" 28 | } 29 | 30 | function check_title() { 31 | local expected=$1 32 | local actual=$2 33 | if [ "$actual" != "$expected" ]; then 34 | fail "Failed to recognize the track (Expected: $expected, Got: $actual)" 35 | else 36 | pass "passed!" 37 | fi 38 | } 39 | 40 | function test_audio_file() { 41 | local extension=$1 42 | local file=$2 43 | local expected_title=$3 44 | 45 | local temp_file="/tmp/test.$extension" 46 | ffmpeg -y -i "$file" -f "$extension" "$temp_file" > /dev/null 2>&1 47 | 48 | info "Testing $temp_file..." 49 | local title 50 | title=$(recognize_audio "$temp_file") 51 | check_title "$expected_title" "$title" 52 | } 53 | 54 | function test_raw_pcm() { 55 | info "Testing raw PCM data..." 56 | echo 57 | 58 | local file=$1 59 | local expected_title=$2 60 | 61 | for type in signed float; do 62 | for bit in 16 24 32 64; do 63 | for rate in 44100 48000; do 64 | for channels in 1 2; do 65 | if [[ "$type" == "float" && "$bit" -lt 32 ]] || [[ "$type" == "signed" && "$bit" -gt 32 ]]; then 66 | continue 67 | fi 68 | codec="${type:0:1}${bit}le" 69 | info " $codec $(echo $rate / 1000 | bc)kHz ${channels}ch..." 70 | 71 | local title 72 | title=$(ffmpeg -i "$file" -f "$codec" -ac $channels -ar $rate - 2>/dev/null | \ 73 | $VIBRA_CLI --recognize --seconds 5 --rate $rate --channels $channels --bits $bit --$type | \ 74 | jq .track.title -r) 75 | check_title "$expected_title" "$title" 76 | sleep "$SHAZAM_REQUEST_DELAY" 77 | done 78 | done 79 | done 80 | done 81 | } 82 | 83 | 84 | function run_tests() { 85 | echo 86 | echo "Running vibra tests..." 87 | echo 88 | 89 | test_audio_file "wav" "$TEST_TARGET" "$TEST_TARGET_TITLE" 90 | test_audio_file "mp3" "$TEST_TARGET" "$TEST_TARGET_TITLE" 91 | test_audio_file "flac" "$TEST_TARGET" "$TEST_TARGET_TITLE" 92 | test_raw_pcm "$TEST_TARGET" "$TEST_TARGET_TITLE" 93 | 94 | echo 95 | if [ $FAILED -eq 1 ]; then 96 | fail "Some tests failed!" 97 | else 98 | pass "All tests passed!" 99 | fi 100 | } 101 | 102 | run_tests 103 | 104 | -------------------------------------------------------------------------------- /lib/vibra.cpp: -------------------------------------------------------------------------------- 1 | #include "../include/vibra.h" 2 | #include "algorithm/signature_generator.h" 3 | #include "audio/downsampler.h" 4 | #include "audio/wav.h" 5 | #include "utils/ffmpeg.h" 6 | 7 | constexpr std::uint32_t MAX_DURATION_SECONDS = 12; 8 | 9 | Fingerprint *_get_fingerprint_from_wav(const Wav &wav); 10 | 11 | Fingerprint *_get_fingerprint_from_low_quality_pcm(const LowQualityTrack &pcm); 12 | 13 | Fingerprint *vibra_get_fingerprint_from_music_file(const char *music_file_path) 14 | { 15 | std::string path = music_file_path; 16 | if (path.size() >= 4 && path.substr(path.size() - 4) == ".wav") 17 | { 18 | Wav wav = Wav::FromFile(path); 19 | return _get_fingerprint_from_wav(wav); 20 | } 21 | 22 | LowQualityTrack pcm = 23 | ffmpeg::FFmpegWrapper::ConvertToLowQaulityPcm(path, 0, MAX_DURATION_SECONDS); 24 | return _get_fingerprint_from_low_quality_pcm(pcm); 25 | } 26 | 27 | Fingerprint *vibra_get_fingerprint_from_wav_data(const char *raw_wav, int wav_data_size) 28 | { 29 | Wav wav = Wav::FromRawWav(raw_wav, wav_data_size); 30 | return _get_fingerprint_from_wav(wav); 31 | } 32 | 33 | Fingerprint *vibra_get_fingerprint_from_signed_pcm(const char *raw_pcm, int pcm_data_size, 34 | int sample_rate, int sample_width, 35 | int channel_count) 36 | { 37 | Wav wav = Wav::FromSignedPCM(raw_pcm, pcm_data_size, sample_rate, sample_width, channel_count); 38 | return _get_fingerprint_from_wav(wav); 39 | } 40 | 41 | Fingerprint *vibra_get_fingerprint_from_float_pcm(const char *raw_pcm, int pcm_data_size, 42 | int sample_rate, int sample_width, 43 | int channel_count) 44 | { 45 | Wav wav = Wav::FromFloatPCM(raw_pcm, pcm_data_size, sample_rate, sample_width, channel_count); 46 | return _get_fingerprint_from_wav(wav); 47 | } 48 | 49 | const char *vibra_get_uri_from_fingerprint(Fingerprint *fingerprint) 50 | { 51 | return fingerprint->uri.c_str(); 52 | } 53 | 54 | unsigned int vibra_get_sample_ms_from_fingerprint(Fingerprint *fingerprint) 55 | { 56 | return fingerprint->sample_ms; 57 | } 58 | 59 | void vibra_free_fingerprint(Fingerprint *fingerprint) 60 | { 61 | delete fingerprint; 62 | } 63 | 64 | Fingerprint *_get_fingerprint_from_wav(const Wav &wav) 65 | { 66 | LowQualityTrack pcm = Downsampler::GetLowQualityPCM(wav); 67 | return _get_fingerprint_from_low_quality_pcm(pcm); 68 | } 69 | 70 | Fingerprint *_get_fingerprint_from_low_quality_pcm(const LowQualityTrack &pcm) 71 | { 72 | SignatureGenerator generator; 73 | generator.FeedInput(pcm); 74 | generator.set_max_time_seconds(MAX_DURATION_SECONDS); 75 | 76 | Signature signature = generator.GetNextSignature(); 77 | 78 | Fingerprint *fingerprint = new Fingerprint; 79 | fingerprint->uri = signature.EncodeBase64(); 80 | fingerprint->sample_ms = signature.num_samples() * 1000 / signature.sample_rate(); 81 | return fingerprint; 82 | } 83 | -------------------------------------------------------------------------------- /lib/algorithm/signature.cpp: -------------------------------------------------------------------------------- 1 | #include "algorithm/signature.h" 2 | #include 3 | #include 4 | #include 5 | #include "utils/base64.h" 6 | #include "utils/crc32.h" 7 | 8 | Signature::Signature(std::uint32_t sample_rate, std::uint32_t num_samples) 9 | : sample_rate_(sample_rate), num_samples_(num_samples) 10 | { 11 | } 12 | 13 | void Signature::Reset(std::uint32_t sampleRate, std::uint32_t num_samples) 14 | { 15 | sample_rate_ = sampleRate; 16 | num_samples_ = num_samples; 17 | frequency_band_to_peaks_.clear(); 18 | } 19 | 20 | std::uint32_t Signature::SumOfPeaksLength() const 21 | { 22 | std::uint32_t sum = 0; 23 | for (const auto &pair : frequency_band_to_peaks_) 24 | { 25 | sum += pair.second.size(); 26 | } 27 | return sum; 28 | } 29 | 30 | std::string Signature::EncodeBase64() const 31 | { 32 | RawSignatureHeader header; 33 | header.magic1 = 0xcafe2580; 34 | header.magic2 = 0x94119c00; 35 | header.shifted_sample_rate_id = 3 << 27; 36 | header.fixed_value = ((15 << 19) + 0x40000); 37 | header.number_samples_plus_divided_sample_rate = 38 | static_cast(num_samples_ + sample_rate_ * 0.24); 39 | std::stringstream contents; 40 | for (const auto &pair : frequency_band_to_peaks_) 41 | { 42 | const auto &band = pair.first; 43 | const auto &peaks = pair.second; 44 | 45 | std::stringstream peak_buf; 46 | std::size_t fft_pass_number = 0; 47 | 48 | for (const auto &peak : peaks) 49 | { 50 | if (peak.fft_pass_number() - fft_pass_number >= 255) 51 | { 52 | peak_buf << "\xff"; 53 | write_little_endian(peak_buf, peak.fft_pass_number()); 54 | fft_pass_number = peak.fft_pass_number(); 55 | } 56 | 57 | peak_buf << static_cast(peak.fft_pass_number() - fft_pass_number); 58 | write_little_endian(peak_buf, peak.peak_magnitude(), 2); 59 | write_little_endian(peak_buf, peak.corrected_peak_frequency_bin(), 2); 60 | 61 | fft_pass_number = peak.fft_pass_number(); 62 | } 63 | 64 | write_little_endian(contents, 0x60030040u + static_cast(band)); 65 | write_little_endian(contents, static_cast(peak_buf.str().size())); 66 | contents << peak_buf.str(); 67 | 68 | for (std::size_t i = 0; i < (-peak_buf.str().size() % 4); ++i) 69 | contents << '\0'; 70 | } 71 | 72 | header.size_minus_header = contents.str().size() + 8; 73 | 74 | std::stringstream header_buf; 75 | header_buf.write(reinterpret_cast(&header), sizeof(header)); 76 | 77 | write_little_endian(header_buf, 0x40000000u); 78 | write_little_endian(header_buf, static_cast(contents.str().size()) + 8); 79 | 80 | header_buf << contents.str(); 81 | 82 | const auto &header_buf_str = header_buf.str(); 83 | header.crc32 = crc32::crc32(header_buf_str.c_str() + 8, header_buf_str.size() - 8) & 0xffffffff; 84 | 85 | header_buf.seekp(0); 86 | header_buf.write(reinterpret_cast(&header), sizeof(header)); 87 | 88 | std::string header_string = header_buf.str(); 89 | 90 | std::string base64_uri; 91 | base64_uri += "data:audio/vnd.shazam.sig;base64,"; 92 | base64_uri += base64::encode(header_string.c_str(), header_string.size()); 93 | return base64_uri; 94 | } 95 | 96 | Signature::~Signature() 97 | { 98 | } 99 | -------------------------------------------------------------------------------- /include/vibra.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_VIBRA_H_ 2 | #define INCLUDE_VIBRA_H_ 3 | 4 | #include 5 | 6 | extern "C" 7 | { 8 | /** 9 | * @brief Structure to hold a music fingerprint. 10 | * 11 | * @note The structure is thread-unsafe and does not require manual memory management 12 | * for the returned pointer. 13 | */ 14 | struct Fingerprint 15 | { 16 | std::string uri; /**< The URI associated with the fingerprint. */ 17 | unsigned int sample_ms; /**< The sample duration in milliseconds. */ 18 | }; 19 | 20 | /** 21 | * @brief Generate a fingerprint from a music file. 22 | * 23 | * @param music_file_path The path to the music file. 24 | * @return Fingerprint* Pointer to the generated fingerprint. 25 | * 26 | * @note The returned pointer must be freed after use. See vibra_free_fingerprint(). 27 | */ 28 | Fingerprint *vibra_get_fingerprint_from_music_file(const char *music_file_path); 29 | 30 | /** 31 | * @brief Generate a fingerprint from WAV data. 32 | * 33 | * @param raw_wav The raw WAV data. 34 | * @param wav_data_size The size of the WAV data in bytes. 35 | * @return Fingerprint* Pointer to the generated fingerprint. 36 | * 37 | * @note The returned pointer must be freed after use. See vibra_free_fingerprint(). 38 | */ 39 | Fingerprint *vibra_get_fingerprint_from_wav_data(const char *raw_wav, int wav_data_size); 40 | 41 | /** 42 | * @brief Generate a fingerprint from signed PCM data. 43 | * 44 | * @param raw_pcm The raw PCM data. 45 | * @param pcm_data_size The size of the PCM data in bytes. 46 | * @param sample_rate The sample rate of the PCM data. 47 | * @param sample_width The sample width (bits per sample) of the PCM data. 48 | * @param channel_count The number of channels in the PCM data. 49 | * @return Fingerprint* Pointer to the generated fingerprint. 50 | * 51 | * @note The returned pointer must be freed after use. See vibra_free_fingerprint(). 52 | */ 53 | Fingerprint *vibra_get_fingerprint_from_signed_pcm(const char *raw_pcm, int pcm_data_size, 54 | int sample_rate, int sample_width, 55 | int channel_count); 56 | 57 | /** 58 | * @brief Generate a fingerprint from PCM data. 59 | * 60 | * @param raw_pcm The raw PCM data. 61 | * @param pcm_data_size The size of the PCM data in bytes. 62 | * @param sample_rate The sample rate of the PCM data. 63 | * @param sample_width The sample width (bits per sample) of the PCM data. 64 | * @param channel_count The number of channels in the PCM data. 65 | * @return Fingerprint* Pointer to the generated fingerprint. 66 | * 67 | * @note The returned pointer must be freed after use. See vibra_free_fingerprint(). 68 | */ 69 | Fingerprint *vibra_get_fingerprint_from_float_pcm(const char *raw_pcm, int pcm_data_size, 70 | int sample_rate, int sample_width, 71 | int channel_count); 72 | 73 | /** 74 | * @brief Get the URI associated with a fingerprint. 75 | * 76 | * @param fingerprint Pointer to the fingerprint. 77 | * @return const char* The URI as a C-string. 78 | * 79 | * @note The returned pointer should not be freed. 80 | */ 81 | const char *vibra_get_uri_from_fingerprint(Fingerprint *fingerprint); 82 | 83 | /** 84 | * @brief Get the sample duration in milliseconds from a fingerprint. 85 | * 86 | * @param fingerprint Pointer to the fingerprint. 87 | * @return unsigned int The sample duration in milliseconds. 88 | */ 89 | unsigned int vibra_get_sample_ms_from_fingerprint(Fingerprint *fingerprint); 90 | 91 | /** 92 | * @brief Free a fingerprint. 93 | * 94 | * @param fingerprint Pointer to the fingerprint. 95 | */ 96 | void vibra_free_fingerprint(Fingerprint *fingerprint); 97 | } // extern "C" 98 | 99 | #endif // INCLUDE_VIBRA_H_ 100 | -------------------------------------------------------------------------------- /lib/utils/ffmpeg.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_UTILS_FFMPEG_H_ 2 | #define LIB_UTILS_FFMPEG_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "audio/downsampler.h" 11 | 12 | #ifdef _MSC_VER 13 | #define PROCESS_OPEN _popen 14 | #define PROCESS_CLOSE _pclose 15 | #else 16 | #define PROCESS_OPEN popen 17 | #define PROCESS_CLOSE pclose 18 | #endif 19 | 20 | namespace ffmpeg 21 | { 22 | 23 | constexpr const char *DEFAULT_FFMPEG_PATHS[] = {"ffmpeg", "ffmpeg.exe"}; 24 | constexpr const char FFMPEG_PATH_ENV[] = "FFMPEG_PATH"; 25 | 26 | class FFmpegWrapper 27 | { 28 | public: 29 | FFmpegWrapper() = delete; 30 | static LowQualityTrack ConvertToLowQaulityPcm( 31 | std::string input_file, std::uint32_t start_seconds, std::uint32_t duration_seconds); 32 | 33 | private: 34 | static std::string getFFmpegPath(); 35 | static bool isWindows(); 36 | }; 37 | 38 | LowQualityTrack FFmpegWrapper::ConvertToLowQaulityPcm( 39 | std::string input_file, std::uint32_t start_seconds, std::uint32_t duration_seconds) 40 | { 41 | static std::string ffmpeg_path = FFmpegWrapper::getFFmpegPath(); 42 | if (ffmpeg_path.empty()) 43 | { 44 | std::cerr << "FFmpeg not found on system. Please install FFmpeg or set the "; 45 | std::cerr << FFMPEG_PATH_ENV << " environment variable." << std::endl; 46 | throw std::runtime_error("FFmpeg not found"); 47 | } 48 | 49 | std::stringstream ss; 50 | ss << ffmpeg_path; 51 | ss << " -i " << input_file; 52 | ss << " -f " 53 | << "s" << LOW_QUALITY_SAMPLE_BIT_WIDTH << "le"; 54 | ss << " -acodec " 55 | << "pcm_s" << LOW_QUALITY_SAMPLE_BIT_WIDTH << "le"; 56 | ss << " -ar " << LOW_QUALITY_SAMPLE_RATE; 57 | ss << " -ac " << 1; 58 | ss << " -ss " << start_seconds; 59 | ss << " -t " << duration_seconds; 60 | ss << " -"; // stdout 61 | ss << " 2>/dev/null"; // suppress std 62 | 63 | std::FILE *pipe = PROCESS_OPEN(ss.str().c_str(), "r"); 64 | if (!pipe) 65 | { 66 | throw std::runtime_error("popen() failed!"); 67 | } 68 | 69 | std::array buffer; 70 | size_t bytes_read; 71 | 72 | LowQualityTrack pcm; 73 | pcm.reserve(duration_seconds * LOW_QUALITY_SAMPLE_RATE); 74 | 75 | while ((bytes_read = fread(buffer.data(), 1, buffer.size(), pipe)) != 0) 76 | { 77 | pcm.insert(pcm.end(), buffer.begin(), 78 | buffer.begin() + (bytes_read / sizeof(LowQualitySample))); 79 | } 80 | 81 | int exit_code = PROCESS_CLOSE(pipe); 82 | if (exit_code != 0) 83 | { 84 | throw std::runtime_error("PCM Conversion Failed: " + std::to_string(exit_code)); 85 | } 86 | return pcm; 87 | } 88 | 89 | std::string FFmpegWrapper::getFFmpegPath() 90 | { 91 | const char *ffmpeg_env = std::getenv(FFMPEG_PATH_ENV); 92 | if (ffmpeg_env) 93 | { 94 | return ffmpeg_env; 95 | } 96 | 97 | std::string path = std::getenv("PATH"); 98 | std::istringstream ss(path); 99 | std::string token; 100 | char delimiter = isWindows() ? ';' : ':'; 101 | while (std::getline(ss, token, delimiter)) 102 | { 103 | for (const char *ffmpeg_path : DEFAULT_FFMPEG_PATHS) 104 | { 105 | std::string full_path = token + "/" + ffmpeg_path; 106 | if (std::ifstream(full_path).good()) 107 | { 108 | return full_path; 109 | } 110 | } 111 | } 112 | return ""; // empty string means FFmpeg not found 113 | } 114 | 115 | bool FFmpegWrapper::isWindows() 116 | { 117 | #if defined(_WIN32) || defined(_WIN64) 118 | return true; 119 | #endif // _WIN32 120 | 121 | return false; 122 | } 123 | 124 | } // namespace ffmpeg 125 | 126 | #endif // LIB_UTILS_FFMPEG_H_ 127 | -------------------------------------------------------------------------------- /lib/audio/wav.cpp: -------------------------------------------------------------------------------- 1 | #include "audio/wav.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | Wav Wav::FromFile(const std::string &wav_file_path) 11 | { 12 | Wav wav; 13 | wav.wav_file_path_ = wav_file_path; 14 | std::ifstream stream(wav_file_path, std::ios::binary); 15 | if (!stream.is_open()) 16 | { 17 | throw std::runtime_error("Failed to open WAV file"); 18 | } 19 | wav.readWavFileBuffer(stream); 20 | return wav; 21 | } 22 | 23 | Wav Wav::FromRawWav(const char *raw_wav, std::uint32_t raw_wav_size) 24 | { 25 | Wav wav; 26 | std::istringstream stream(std::string(raw_wav, raw_wav_size)); 27 | wav.readWavFileBuffer(stream); 28 | return wav; 29 | } 30 | 31 | Wav Wav::FromSignedPCM(const char *raw_pcm, std::uint32_t raw_pcm_size, std::uint32_t sample_rate, 32 | std::uint32_t sample_width, std::uint32_t channel_count) 33 | { 34 | return fromPCM(raw_pcm, raw_pcm_size, AudioFormat::PCM_INTEGER, sample_rate, sample_width, 35 | channel_count); 36 | } 37 | 38 | Wav Wav::FromFloatPCM(const char *raw_pcm, std::uint32_t raw_pcm_size, std::uint32_t sample_rate, 39 | std::uint32_t sample_width, std::uint32_t channel_count) 40 | { 41 | return fromPCM(raw_pcm, raw_pcm_size, AudioFormat::PCM_FLOAT, sample_rate, sample_width, 42 | channel_count); 43 | } 44 | 45 | Wav::~Wav() 46 | { 47 | } 48 | 49 | Wav Wav::fromPCM(const char *raw_pcm, std::uint32_t raw_pcm_size, AudioFormat audio_format, 50 | std::uint32_t sample_rate, std::uint32_t sample_width, std::uint32_t channel_count) 51 | { 52 | Wav wav; 53 | wav.header_.file_size = sizeof(WavHeader) + sizeof(FmtSubchunk) + 8 + raw_pcm_size; 54 | wav.fmt_.audio_format = static_cast(audio_format); 55 | wav.fmt_.num_channels = channel_count; 56 | wav.fmt_.sample_rate = sample_rate; 57 | wav.fmt_.byte_rate = sample_rate * channel_count * sample_width / 8; 58 | wav.fmt_.block_align = channel_count * sample_width / 8; 59 | wav.fmt_.bits_per_sample = sample_width; 60 | wav.data_size_ = raw_pcm_size; 61 | wav.data_.reset(new std::uint8_t[raw_pcm_size]); 62 | std::memcpy(wav.data_.get(), raw_pcm, raw_pcm_size); 63 | return wav; 64 | } 65 | 66 | void Wav::readWavFileBuffer(std::istream &stream) 67 | { 68 | stream.read(reinterpret_cast(&header_), sizeof(WavHeader)); 69 | 70 | const auto kSubchunkLimit = 10; 71 | 72 | bool data_chunk_found = false; 73 | bool fmt_chunk_found = false; 74 | for (int i = 0; i < kSubchunkLimit && stream.tellg() < header_.file_size - 8; i++) 75 | { 76 | char subchunk_id[4]; 77 | stream.read(subchunk_id, 4); 78 | 79 | std::uint32_t subchunk_size; 80 | stream.read(reinterpret_cast(&subchunk_size), 4); 81 | 82 | if (strncmp(subchunk_id, "data", 4) == 0) 83 | { 84 | data_size_ = subchunk_size; 85 | data_.reset(new std::uint8_t[data_size_]); 86 | stream.read(reinterpret_cast(data_.get()), data_size_); 87 | data_chunk_found = true; 88 | } 89 | else if (strncmp(subchunk_id, "fmt ", 4) == 0) 90 | { 91 | stream.read(reinterpret_cast(&fmt_), sizeof(FmtSubchunk)); 92 | fmt_chunk_found = true; 93 | } 94 | else 95 | { 96 | stream.seekg(subchunk_size, std::ios::cur); 97 | } 98 | 99 | if (data_chunk_found && fmt_chunk_found) 100 | { 101 | return; // read wav successfully 102 | } 103 | } 104 | 105 | if (!data_chunk_found || !fmt_chunk_found) 106 | { 107 | throw std::runtime_error("Invalid WAV file"); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /cli/communication/shazam.cpp: -------------------------------------------------------------------------------- 1 | #include "communication/shazam.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "communication/timezones.h" 7 | #include "communication/user_agents.h" 8 | #include "utils/uuid4.h" 9 | #include "../../include/vibra.h" 10 | 11 | // static variables initialization 12 | constexpr char Shazam::HOST[]; 13 | 14 | std::size_t writeCallback(void *contents, size_t size, size_t nmemb, void *userp) 15 | { 16 | std::string *buffer = reinterpret_cast(userp); 17 | std::size_t realsize = size * nmemb; 18 | buffer->append(reinterpret_cast(contents), realsize); 19 | return realsize; 20 | } 21 | 22 | std::string Shazam::Recognize(const Fingerprint *fingerprint) 23 | { 24 | auto content = getRequestContent(fingerprint->uri, fingerprint->sample_ms); 25 | auto user_agent = getUserAgent(); 26 | std::string url = getShazamHost(); 27 | 28 | CURL *curl = curl_easy_init(); 29 | std::string read_buffer; 30 | 31 | if (curl) 32 | { 33 | struct curl_slist *headers = nullptr; 34 | headers = curl_slist_append(headers, "Accept-Encoding: gzip, deflate, br"); 35 | headers = curl_slist_append(headers, "Accept: */*"); 36 | headers = curl_slist_append(headers, "Connection: keep-alive"); 37 | headers = curl_slist_append(headers, "Content-Type: application/json"); 38 | headers = curl_slist_append(headers, "Content-Language: en_US"); 39 | curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); 40 | curl_easy_setopt(curl, CURLOPT_POST, 1L); 41 | curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent.c_str()); 42 | curl_easy_setopt(curl, CURLOPT_POSTFIELDS, content.c_str()); 43 | curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); 44 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback); 45 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, &read_buffer); 46 | 47 | curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "gzip, deflate, br"); 48 | curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 49 | 50 | CURLcode res = curl_easy_perform(curl); 51 | if (res != CURLE_OK) 52 | { 53 | std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl; 54 | } 55 | 56 | std::int64_t http_code = 0; 57 | curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); 58 | if (http_code != 200) 59 | { 60 | std::cerr << "HTTP code: " << http_code << std::endl; 61 | } 62 | curl_slist_free_all(headers); 63 | curl_easy_cleanup(curl); 64 | } 65 | return read_buffer; 66 | } 67 | 68 | std::string Shazam::getShazamHost() 69 | { 70 | std::string host = HOST + uuid4::generate() + "/" + uuid4::generate(); 71 | host += "?sync=true&" 72 | "webv3=true&" 73 | "sampling=true&" 74 | "connected=&" 75 | "shazamapiversion=v3&" 76 | "sharehub=true&" 77 | "video=v3"; 78 | return host; 79 | } 80 | 81 | std::string Shazam::getRequestContent(const std::string &uri, unsigned int sample_ms) 82 | { 83 | std::mt19937 gen(std::random_device{}()); 84 | std::uniform_int_distribution<> dis_float(0.0, 1.0); 85 | 86 | auto timezone = getTimezone(); 87 | double fuzz = dis_float(gen) * 15.3 - 7.65; 88 | 89 | std::stringstream json_buf; 90 | json_buf << "{"; 91 | json_buf << "\"geolocation\":{"; 92 | json_buf << "\"altitude\":" << dis_float(gen) * 400 + 100 + fuzz << ","; 93 | json_buf << "\"latitude\":" << dis_float(gen) * 180 - 90 + fuzz << ","; 94 | json_buf << "\"longitude\":" << dis_float(gen) * 360 - 180 + fuzz; 95 | json_buf << "},"; 96 | json_buf << "\"signature\":{"; 97 | json_buf << "\"samplems\":" << sample_ms << ","; 98 | json_buf << "\"timestamp\":" << time(nullptr) * 1000ULL << ","; 99 | json_buf << "\"uri\":\"" << uri << "\""; 100 | json_buf << "},"; 101 | json_buf << "\"timestamp\":" << time(nullptr) * 1000ULL << ","; 102 | json_buf << "\"timezone\":" 103 | << "\"" << timezone << "\""; 104 | json_buf << "}"; 105 | std::string content = json_buf.str(); 106 | return content; 107 | } 108 | 109 | std::string Shazam::getUserAgent() 110 | { 111 | std::mt19937 gen(std::random_device{}()); 112 | std::uniform_int_distribution<> dis_useragent(0, USER_AGENTS_SIZE - 1); 113 | return USER_AGENTS[dis_useragent(gen)]; 114 | } 115 | 116 | std::string Shazam::getTimezone() 117 | { 118 | std::mt19937 gen(std::random_device{}()); 119 | std::uniform_int_distribution<> dis_timezone(0, EUROPE_TIMEZONES_SIZE - 1); 120 | return EUROPE_TIMEZONES[dis_timezone(gen)]; 121 | } 122 | -------------------------------------------------------------------------------- /cli/cli.cpp: -------------------------------------------------------------------------------- 1 | #include "../cli/cli.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "communication/shazam.h" 7 | 8 | int CLI::Run(int argc, char **argv) 9 | { 10 | args::ArgumentParser parser(""); 11 | parser.SetArgumentSeparations(false, false, true, true); 12 | 13 | parser.helpParams.width = 200; 14 | parser.helpParams.progindent = 0; 15 | parser.helpParams.progtailindent = 0; 16 | parser.helpParams.flagindent = 2; 17 | parser.helpParams.descriptionindent = 2; 18 | parser.helpParams.eachgroupindent = 4; 19 | parser.helpParams.showValueName = false; 20 | parser.helpParams.optionsString = "Options:"; 21 | parser.helpParams.proglineOptions = "{COMMAND} [OPTIONS]"; 22 | 23 | args::Group actions(parser, "Commands:", args::Group::Validators::Xor); 24 | args::Flag fingerprint_only(actions, "fingerprint", "Generate a fingerprint", 25 | {'F', "fingerprint"}); 26 | args::Flag recognize(actions, "recognize", "Recognize a song", {'R', "recognize"}); 27 | args::HelpFlag help(actions, "help", "Display this help menu", {'h', "help"}); 28 | 29 | args::Group sources(parser, "Sources:", args::Group::Validators::Xor); 30 | 31 | args::Group file_sources(sources, "File sources:", args::Group::Validators::Xor); 32 | args::ValueFlag music_file(file_sources, "file", 33 | "FFmpeg required for non-wav files", {'f', "file"}); 34 | 35 | args::Group raw_sources(sources, "Raw PCM sources:", args::Group::Validators::All); 36 | args::ValueFlag chunk_seconds(raw_sources, "seconds", "Chunk seconds", {'s', "seconds"}); 37 | args::ValueFlag sample_rate(raw_sources, "rate", "Sample rate", {'r', "rate"}); 38 | args::ValueFlag channels(raw_sources, "channels", "Channels", {'c', "channels"}); 39 | args::ValueFlag bits_per_sample(raw_sources, "bits", "Bits per sample", {'b', "bits"}); 40 | 41 | args::Group source_type(raw_sources, "Source type:", args::Group::Validators::AtMostOne); 42 | args::Flag signed_pcm(source_type, "signed", "Signed PCM (default)", {'S', "signed"}); 43 | args::Flag float_pcm(source_type, "float", "Float PCM", {'D', "float"}); 44 | 45 | try 46 | { 47 | parser.ParseCLI(argc, argv); 48 | } 49 | catch (const args::Help &) 50 | { 51 | std::cout << parser; 52 | return 0; 53 | } 54 | catch (const std::runtime_error &e) 55 | { 56 | std::cerr << std::endl; 57 | std::cerr << e.what() << std::endl; 58 | std::cerr << std::endl; 59 | std::cerr << parser; 60 | return 1; 61 | } 62 | 63 | Fingerprint *fingerprint = nullptr; 64 | if (music_file) 65 | { 66 | std::string file = args::get(music_file); 67 | fingerprint = getFingerprintFromMusicFile(file); 68 | } 69 | else if (chunk_seconds && sample_rate && channels && bits_per_sample) 70 | { 71 | bool is_signed = signed_pcm || !float_pcm; 72 | fingerprint = 73 | getFingerprintFromStdin(args::get(chunk_seconds), args::get(sample_rate), 74 | args::get(channels), args::get(bits_per_sample), is_signed); 75 | } 76 | else 77 | { 78 | std::cerr << "Invalid arguments" << std::endl; 79 | return 1; 80 | } 81 | 82 | if (fingerprint_only) 83 | { 84 | std::cout << fingerprint->uri << std::endl; 85 | } 86 | else if (recognize) 87 | { 88 | std::cout << Shazam::Recognize(fingerprint) << std::endl; 89 | } 90 | return 0; 91 | } 92 | 93 | Fingerprint *CLI::getFingerprintFromMusicFile(const std::string &music_file) 94 | { 95 | if (std::ifstream(music_file).good() == false) 96 | { 97 | std::cerr << "File not found: " << music_file << std::endl; 98 | throw std::ifstream::failure("File not found"); 99 | } 100 | return vibra_get_fingerprint_from_music_file(music_file.c_str()); 101 | } 102 | 103 | Fingerprint *CLI::getFingerprintFromStdin(int chunk_seconds, int sample_rate, int channels, 104 | int bits_per_sample, bool is_signed) 105 | { 106 | std::size_t bytes = chunk_seconds * sample_rate * channels * (bits_per_sample / 8); 107 | std::vector buffer(bytes); 108 | std::cin.read(buffer.data(), bytes); 109 | if (is_signed) 110 | { 111 | return vibra_get_fingerprint_from_signed_pcm(buffer.data(), bytes, sample_rate, 112 | bits_per_sample, channels); 113 | } 114 | return vibra_get_fingerprint_from_float_pcm(buffer.data(), bytes, sample_rate, bits_per_sample, 115 | channels); 116 | } 117 | -------------------------------------------------------------------------------- /bindings/wasm/src/uger_agent.ts: -------------------------------------------------------------------------------- 1 | export const USER_AGENTS = [ 2 | "Dalvik/2.1.0 (Linux; U; Android 5.0.2; VS980 4G Build/LRX22G)", 3 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; SM-T210 Build/KOT49H)", 4 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-P905V Build/LMY47X)", 5 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; Vodafone Smart Tab 4G Build/KTU84P)", 6 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; SM-G360H Build/KTU84P)", 7 | "Dalvik/2.1.0 (Linux; U; Android 5.0.2; SM-S920L Build/LRX22G)", 8 | "Dalvik/2.1.0 (Linux; U; Android 5.0; Fire Pro Build/LRX21M)", 9 | "Dalvik/2.1.0 (Linux; U; Android 5.0; SM-N9005 Build/LRX21V)", 10 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; SM-G920F Build/MMB29K)", 11 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; SM-G7102 Build/KOT49H)", 12 | "Dalvik/2.1.0 (Linux; U; Android 5.0; SM-G900F Build/LRX21T)", 13 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; SM-G928F Build/MMB29K)", 14 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-J500FN Build/LMY48B)", 15 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; Coolpad 3320A Build/LMY47V)", 16 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; SM-J110F Build/KTU84P)", 17 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; SAMSUNG-SGH-I747 Build/KOT49H)", 18 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; SAMSUNG-SM-T337A Build/KOT49H)", 19 | "Dalvik/1.6.0 (Linux; U; Android 4.3; SGH-T999 Build/JSS15J)", 20 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; D6603 Build/23.5.A.0.570)", 21 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-J700H Build/LMY48B)", 22 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; HTC6600LVW Build/KOT49H)", 23 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-N910G Build/LMY47X)", 24 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-N910T Build/LMY47X)", 25 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; C6903 Build/14.4.A.0.157)", 26 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; SM-G920F Build/MMB29K)", 27 | "Dalvik/1.6.0 (Linux; U; Android 4.2.2; GT-I9105P Build/JDQ39)", 28 | "Dalvik/2.1.0 (Linux; U; Android 5.0; SM-G900F Build/LRX21T)", 29 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; GT-I9192 Build/KOT49H)", 30 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-G531H Build/LMY48B)", 31 | "Dalvik/2.1.0 (Linux; U; Android 5.0; SM-N9005 Build/LRX21V)", 32 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; LGMS345 Build/LMY47V)", 33 | "Dalvik/2.1.0 (Linux; U; Android 5.0.2; HTC One Build/LRX22G)", 34 | "Dalvik/2.1.0 (Linux; U; Android 5.0.2; LG-D800 Build/LRX22G)", 35 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-G531H Build/LMY48B)", 36 | "Dalvik/2.1.0 (Linux; U; Android 5.0; SM-N9005 Build/LRX21V)", 37 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; SM-T113 Build/KTU84P)", 38 | "Dalvik/1.6.0 (Linux; U; Android 4.2.2; AndyWin Build/JDQ39E)", 39 | "Dalvik/2.1.0 (Linux; U; Android 5.0; Lenovo A7000-a Build/LRX21M)", 40 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; LGL16C Build/KOT49I.L16CV11a)", 41 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; GT-I9500 Build/KOT49H)", 42 | "Dalvik/2.1.0 (Linux; U; Android 5.0.2; SM-A700FD Build/LRX22G)", 43 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; SM-G130HN Build/KOT49H)", 44 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; SM-N9005 Build/KOT49H)", 45 | "Dalvik/1.6.0 (Linux; U; Android 4.1.2; LG-E975T Build/JZO54K)", 46 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; E1 Build/KOT49H)", 47 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; GT-I9500 Build/KOT49H)", 48 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; GT-N5100 Build/KOT49H)", 49 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-A310F Build/LMY47X)", 50 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-J105H Build/LMY47V)", 51 | "Dalvik/1.6.0 (Linux; U; Android 4.3; GT-I9305T Build/JSS15J)", 52 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; android Build/JDQ39)", 53 | "Dalvik/1.6.0 (Linux; U; Android 4.2.1; HS-U970 Build/JOP40D)", 54 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; SM-T561 Build/KTU84P)", 55 | "Dalvik/1.6.0 (Linux; U; Android 4.2.2; GT-P3110 Build/JDQ39)", 56 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; SM-G925T Build/MMB29K)", 57 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; HUAWEI Y221-U22 Build/HUAWEIY221-U22)", 58 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-G530T1 Build/LMY47X)", 59 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-G920I Build/LMY47X)", 60 | "Dalvik/2.1.0 (Linux; U; Android 5.0; SM-G900F Build/LRX21T)", 61 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; Vodafone Smart ultra 6 Build/LMY47V)", 62 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; XT1080 Build/SU6-7.7)", 63 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; ASUS MeMO Pad 7 Build/KTU84P)", 64 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; SM-G800F Build/KOT49H)", 65 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; GT-N7100 Build/KOT49H)", 66 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; SM-G925I Build/MMB29K)", 67 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; A0001 Build/MMB29X)", 68 | "Dalvik/2.1.0 (Linux; U; Android 5.1; XT1045 Build/LPB23.13-61)", 69 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; LGMS330 Build/LMY47V)", 70 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; Z970 Build/KTU84P)", 71 | "Dalvik/2.1.0 (Linux; U; Android 5.0; SM-N900P Build/LRX21V)", 72 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; T1-701u Build/HuaweiMediaPad)", 73 | "Dalvik/2.1.0 (Linux; U; Android 5.1; HTCD100LVWPP Build/LMY47O)", 74 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; SM-G935R4 Build/MMB29M)", 75 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; SM-G930V Build/MMB29M)", 76 | "Dalvik/2.1.0 (Linux; U; Android 5.0.2; ZTE Blade Q Lux Build/LRX22G)", 77 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; GT-I9060I Build/KTU84P)", 78 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; LGUS992 Build/MMB29M)", 79 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; SM-G900P Build/MMB29M)", 80 | "Dalvik/1.6.0 (Linux; U; Android 4.1.2; SGH-T999L Build/JZO54K)", 81 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-N910V Build/LMY47X)", 82 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; GT-I9500 Build/KOT49H)", 83 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-P601 Build/LMY47X)", 84 | "Dalvik/1.6.0 (Linux; U; Android 4.2.2; GT-S7272 Build/JDQ39)", 85 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-N910T Build/LMY47X)", 86 | "Dalvik/1.6.0 (Linux; U; Android 4.3; SAMSUNG-SGH-I747 Build/JSS15J)", 87 | "Dalvik/2.1.0 (Linux; U; Android 5.0.2; ZTE Blade Q Lux Build/LRX22G)", 88 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; SM-G930F Build/MMB29K)", 89 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; HTC_PO582 Build/KOT49H)", 90 | "Dalvik/2.1.0 (Linux; U; Android 6.0; HUAWEI MT7-TL10 Build/HuaweiMT7-TL10)", 91 | "Dalvik/2.1.0 (Linux; U; Android 6.0; LG-H811 Build/MRA58K)", 92 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; SM-N7505 Build/KOT49H)", 93 | "Dalvik/2.1.0 (Linux; U; Android 6.0; LG-H815 Build/MRA58K)", 94 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; LenovoA3300-HV Build/KOT49H)", 95 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; SM-G360G Build/KTU84P)", 96 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; GT-I9300I Build/KTU84P)", 97 | "Dalvik/2.1.0 (Linux; U; Android 5.0; SM-G900F Build/LRX21T)", 98 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; SM-J700T Build/MMB29K)", 99 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-J500FN Build/LMY48B)", 100 | "Dalvik/1.6.0 (Linux; U; Android 4.2.2; SM-T217S Build/JDQ39)", 101 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; SAMSUNG-SM-N900A Build/KTU84P)" 102 | ] -------------------------------------------------------------------------------- /cli/communication/user_agents.h: -------------------------------------------------------------------------------- 1 | #ifndef CLI_COMMUNICATION_USER_AGENTS_H_ 2 | #define CLI_COMMUNICATION_USER_AGENTS_H_ 3 | 4 | // 5 | // https://github.com/SaswatPadhi/FlashProfileDemo/blob/c1e3f05d09f6443568a606dc0a439d6ebb057ae1/tests/hetero/user_agents.json 6 | // 7 | 8 | static constexpr const char *USER_AGENTS[] = { 9 | "Dalvik/2.1.0 (Linux; U; Android 5.0.2; VS980 4G Build/LRX22G)", 10 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; SM-T210 Build/KOT49H)", 11 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-P905V Build/LMY47X)", 12 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; Vodafone Smart Tab 4G Build/KTU84P)", 13 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; SM-G360H Build/KTU84P)", 14 | "Dalvik/2.1.0 (Linux; U; Android 5.0.2; SM-S920L Build/LRX22G)", 15 | "Dalvik/2.1.0 (Linux; U; Android 5.0; Fire Pro Build/LRX21M)", 16 | "Dalvik/2.1.0 (Linux; U; Android 5.0; SM-N9005 Build/LRX21V)", 17 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; SM-G920F Build/MMB29K)", 18 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; SM-G7102 Build/KOT49H)", 19 | "Dalvik/2.1.0 (Linux; U; Android 5.0; SM-G900F Build/LRX21T)", 20 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; SM-G928F Build/MMB29K)", 21 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-J500FN Build/LMY48B)", 22 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; Coolpad 3320A Build/LMY47V)", 23 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; SM-J110F Build/KTU84P)", 24 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; SAMSUNG-SGH-I747 Build/KOT49H)", 25 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; SAMSUNG-SM-T337A Build/KOT49H)", 26 | "Dalvik/1.6.0 (Linux; U; Android 4.3; SGH-T999 Build/JSS15J)", 27 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; D6603 Build/23.5.A.0.570)", 28 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-J700H Build/LMY48B)", 29 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; HTC6600LVW Build/KOT49H)", 30 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-N910G Build/LMY47X)", 31 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-N910T Build/LMY47X)", 32 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; C6903 Build/14.4.A.0.157)", 33 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; SM-G920F Build/MMB29K)", 34 | "Dalvik/1.6.0 (Linux; U; Android 4.2.2; GT-I9105P Build/JDQ39)", 35 | "Dalvik/2.1.0 (Linux; U; Android 5.0; SM-G900F Build/LRX21T)", 36 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; GT-I9192 Build/KOT49H)", 37 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-G531H Build/LMY48B)", 38 | "Dalvik/2.1.0 (Linux; U; Android 5.0; SM-N9005 Build/LRX21V)", 39 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; LGMS345 Build/LMY47V)", 40 | "Dalvik/2.1.0 (Linux; U; Android 5.0.2; HTC One Build/LRX22G)", 41 | "Dalvik/2.1.0 (Linux; U; Android 5.0.2; LG-D800 Build/LRX22G)", 42 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-G531H Build/LMY48B)", 43 | "Dalvik/2.1.0 (Linux; U; Android 5.0; SM-N9005 Build/LRX21V)", 44 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; SM-T113 Build/KTU84P)", 45 | "Dalvik/1.6.0 (Linux; U; Android 4.2.2; AndyWin Build/JDQ39E)", 46 | "Dalvik/2.1.0 (Linux; U; Android 5.0; Lenovo A7000-a Build/LRX21M)", 47 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; LGL16C Build/KOT49I.L16CV11a)", 48 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; GT-I9500 Build/KOT49H)", 49 | "Dalvik/2.1.0 (Linux; U; Android 5.0.2; SM-A700FD Build/LRX22G)", 50 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; SM-G130HN Build/KOT49H)", 51 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; SM-N9005 Build/KOT49H)", 52 | "Dalvik/1.6.0 (Linux; U; Android 4.1.2; LG-E975T Build/JZO54K)", 53 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; E1 Build/KOT49H)", 54 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; GT-I9500 Build/KOT49H)", 55 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; GT-N5100 Build/KOT49H)", 56 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-A310F Build/LMY47X)", 57 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-J105H Build/LMY47V)", 58 | "Dalvik/1.6.0 (Linux; U; Android 4.3; GT-I9305T Build/JSS15J)", 59 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; android Build/JDQ39)", 60 | "Dalvik/1.6.0 (Linux; U; Android 4.2.1; HS-U970 Build/JOP40D)", 61 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; SM-T561 Build/KTU84P)", 62 | "Dalvik/1.6.0 (Linux; U; Android 4.2.2; GT-P3110 Build/JDQ39)", 63 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; SM-G925T Build/MMB29K)", 64 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; HUAWEI Y221-U22 Build/HUAWEIY221-U22)", 65 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-G530T1 Build/LMY47X)", 66 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-G920I Build/LMY47X)", 67 | "Dalvik/2.1.0 (Linux; U; Android 5.0; SM-G900F Build/LRX21T)", 68 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; Vodafone Smart ultra 6 Build/LMY47V)", 69 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; XT1080 Build/SU6-7.7)", 70 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; ASUS MeMO Pad 7 Build/KTU84P)", 71 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; SM-G800F Build/KOT49H)", 72 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; GT-N7100 Build/KOT49H)", 73 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; SM-G925I Build/MMB29K)", 74 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; A0001 Build/MMB29X)", 75 | "Dalvik/2.1.0 (Linux; U; Android 5.1; XT1045 Build/LPB23.13-61)", 76 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; LGMS330 Build/LMY47V)", 77 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; Z970 Build/KTU84P)", 78 | "Dalvik/2.1.0 (Linux; U; Android 5.0; SM-N900P Build/LRX21V)", 79 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; T1-701u Build/HuaweiMediaPad)", 80 | "Dalvik/2.1.0 (Linux; U; Android 5.1; HTCD100LVWPP Build/LMY47O)", 81 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; SM-G935R4 Build/MMB29M)", 82 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; SM-G930V Build/MMB29M)", 83 | "Dalvik/2.1.0 (Linux; U; Android 5.0.2; ZTE Blade Q Lux Build/LRX22G)", 84 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; GT-I9060I Build/KTU84P)", 85 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; LGUS992 Build/MMB29M)", 86 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; SM-G900P Build/MMB29M)", 87 | "Dalvik/1.6.0 (Linux; U; Android 4.1.2; SGH-T999L Build/JZO54K)", 88 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-N910V Build/LMY47X)", 89 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; GT-I9500 Build/KOT49H)", 90 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-P601 Build/LMY47X)", 91 | "Dalvik/1.6.0 (Linux; U; Android 4.2.2; GT-S7272 Build/JDQ39)", 92 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-N910T Build/LMY47X)", 93 | "Dalvik/1.6.0 (Linux; U; Android 4.3; SAMSUNG-SGH-I747 Build/JSS15J)", 94 | "Dalvik/2.1.0 (Linux; U; Android 5.0.2; ZTE Blade Q Lux Build/LRX22G)", 95 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; SM-G930F Build/MMB29K)", 96 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; HTC_PO582 Build/KOT49H)", 97 | "Dalvik/2.1.0 (Linux; U; Android 6.0; HUAWEI MT7-TL10 Build/HuaweiMT7-TL10)", 98 | "Dalvik/2.1.0 (Linux; U; Android 6.0; LG-H811 Build/MRA58K)", 99 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; SM-N7505 Build/KOT49H)", 100 | "Dalvik/2.1.0 (Linux; U; Android 6.0; LG-H815 Build/MRA58K)", 101 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; LenovoA3300-HV Build/KOT49H)", 102 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; SM-G360G Build/KTU84P)", 103 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; GT-I9300I Build/KTU84P)", 104 | "Dalvik/2.1.0 (Linux; U; Android 5.0; SM-G900F Build/LRX21T)", 105 | "Dalvik/2.1.0 (Linux; U; Android 6.0.1; SM-J700T Build/MMB29K)", 106 | "Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-J500FN Build/LMY48B)", 107 | "Dalvik/1.6.0 (Linux; U; Android 4.2.2; SM-T217S Build/JDQ39)", 108 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; SAMSUNG-SM-N900A Build/KTU84P)"}; 109 | 110 | constexpr unsigned int USER_AGENTS_SIZE = sizeof(USER_AGENTS) / sizeof(USER_AGENTS[0]); 111 | 112 | #endif // CLI_COMMUNICATION_USER_AGENTS_H_ 113 | -------------------------------------------------------------------------------- /lib/audio/downsampler.cpp: -------------------------------------------------------------------------------- 1 | #include "audio/downsampler.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "audio/byte_control.h" 7 | #include "audio/wav.h" 8 | 9 | LowQualityTrack Downsampler::GetLowQualityPCM(const Wav &wav, std::int32_t start_sec, 10 | std::int32_t end_sec) 11 | { 12 | LowQualityTrack low_quality_pcm; 13 | 14 | const auto channels = wav.num_channels(); 15 | const auto sample_rate = wav.sample_rate_(); 16 | const auto bits_per_sample = wav.bits_per_sample(); 17 | const auto data_size = wav.data_size(); 18 | const auto audio_format = wav.audio_format(); 19 | const std::uint8_t *pcm_data = wav.data().get(); 20 | 21 | if (channels == 1 && sample_rate == LOW_QUALITY_SAMPLE_RATE && 22 | bits_per_sample == LOW_QUALITY_SAMPLE_BIT_WIDTH && start_sec == 0 && end_sec == -1) 23 | { 24 | // no need to convert low quality pcm. just copy raw data 25 | low_quality_pcm.resize(data_size); 26 | std::memcpy(low_quality_pcm.data(), wav.data().get(), data_size); 27 | return low_quality_pcm; 28 | } 29 | 30 | double downsample_ratio = sample_rate / static_cast(LOW_QUALITY_SAMPLE_RATE); 31 | std::uint32_t width = bits_per_sample / 8; 32 | std::uint32_t sample_count = data_size / width; 33 | 34 | const void *src_raw_data = pcm_data + (start_sec * sample_rate * width * channels); 35 | 36 | std::uint32_t new_sample_count = sample_count / channels / downsample_ratio; 37 | 38 | if (end_sec != -1) 39 | { 40 | new_sample_count = (end_sec - start_sec) * LOW_QUALITY_SAMPLE_RATE; 41 | } 42 | 43 | low_quality_pcm.resize(new_sample_count); 44 | 45 | auto downsample_func = &Downsampler::signedMonoToMono; 46 | bool is_signed = audio_format == 1; 47 | 48 | downsample_func = getDownsampleFunc(is_signed, bits_per_sample, channels); 49 | downsample_func(&low_quality_pcm, src_raw_data, downsample_ratio, new_sample_count, width, 50 | channels); 51 | return low_quality_pcm; 52 | } 53 | 54 | DownsampleFunc Downsampler::getDownsampleFunc(bool is_signed, std::uint32_t width, 55 | std::uint32_t channels) 56 | { 57 | channels = std::min(channels, 3u); 58 | width = is_signed ? 0 : width; 59 | 60 | static std::map, DownsampleFunc> func_map{ 61 | {std::make_tuple(true, 0, 1), &Downsampler::signedMonoToMono}, 62 | {std::make_tuple(true, 0, 2), &Downsampler::signedStereoToMono}, 63 | {std::make_tuple(true, 0, 3), &Downsampler::signedMultiToMono}, 64 | {std::make_tuple(false, 32, 1), &Downsampler::floatMonoToMono}, 65 | {std::make_tuple(false, 32, 2), &Downsampler::floatStereoToMono}, 66 | {std::make_tuple(false, 32, 3), &Downsampler::floatMultiToMono}, 67 | {std::make_tuple(false, 64, 1), &Downsampler::floatMonoToMono}, 68 | {std::make_tuple(false, 64, 2), &Downsampler::floatStereoToMono}, 69 | {std::make_tuple(false, 64, 3), &Downsampler::floatMultiToMono}, 70 | }; 71 | return func_map.at(std::make_tuple(is_signed, width, channels)); 72 | } 73 | 74 | void Downsampler::signedMonoToMono(LowQualityTrack *dst, const void *src, double downsample_ratio, 75 | std::uint32_t new_sample_count, std::uint32_t width, 76 | std::uint32_t channels) 77 | { 78 | std::uint32_t index = 0; 79 | for (std::uint32_t i = 0; i < new_sample_count; i++) 80 | { 81 | index = std::uint32_t(i * downsample_ratio) * width * channels; 82 | dst->at(i) = GETSAMPLE64(width, src, index) >> (64 - LOW_QUALITY_SAMPLE_BIT_WIDTH); 83 | } 84 | } 85 | 86 | void Downsampler::signedStereoToMono(LowQualityTrack *dst, const void *src, double downsample_ratio, 87 | std::uint32_t new_sample_count, std::uint32_t width, 88 | std::uint32_t channels) 89 | { 90 | std::uint32_t index = 0; 91 | for (std::uint32_t i = 0; i < new_sample_count; i++) 92 | { 93 | index = std::uint32_t(i * downsample_ratio) * width * channels; 94 | LowQualitySample sample1 = 95 | GETSAMPLE64(width, src, index) >> (64 - LOW_QUALITY_SAMPLE_BIT_WIDTH); 96 | LowQualitySample sample2 = 97 | GETSAMPLE64(width, src, index + width) >> (64 - LOW_QUALITY_SAMPLE_BIT_WIDTH); 98 | dst->at(i) = (sample1 + sample2) / 2; 99 | } 100 | } 101 | 102 | void Downsampler::signedMultiToMono(LowQualityTrack *dst, const void *src, double downsample_ratio, 103 | std::uint32_t new_sample_count, std::uint32_t width, 104 | std::uint32_t channels) 105 | { 106 | double collected_sample = 0; 107 | std::uint32_t index = 0; 108 | for (std::uint32_t i = 0; i < new_sample_count; i++) 109 | { 110 | collected_sample = 0; 111 | for (std::uint32_t k = 0; k < channels; k++) 112 | { 113 | index = std::uint32_t(i * downsample_ratio) * width * channels; 114 | collected_sample += 115 | GETSAMPLE64(width, src, index + k * width) >> (64 - LOW_QUALITY_SAMPLE_BIT_WIDTH); 116 | } 117 | dst->at(i) = LowQualitySample(collected_sample / channels); 118 | } 119 | } 120 | 121 | template 122 | void Downsampler::floatMonoToMono(LowQualityTrack *dst, const void *src, double downsample_ratio, 123 | std::uint32_t new_sample_count, std::uint32_t width, 124 | std::uint32_t channels) 125 | { 126 | T temp_float_sample = 0; 127 | std::uint64_t temp_sample = 0; 128 | std::uint32_t index = 0; 129 | for (std::uint32_t i = 0; i < new_sample_count; i++) 130 | { 131 | index = std::uint32_t(i * downsample_ratio) * width * channels; 132 | temp_sample = GETSAMPLE64(width, src, index) >> (64 - sizeof(T) * 8); 133 | temp_float_sample = *reinterpret_cast(&temp_sample); 134 | dst->at(i) = LowQualitySample(temp_float_sample * LOW_QUALITY_SAMPLE_MAX); 135 | } 136 | } 137 | 138 | template 139 | void Downsampler::floatStereoToMono(LowQualityTrack *dst, const void *src, double downsample_ratio, 140 | std::uint32_t new_sample_count, std::uint32_t width, 141 | std::uint32_t channels) 142 | { 143 | std::uint64_t temp_sample1 = 0; 144 | std::uint64_t temp_sample2 = 0; 145 | T temp_float_sample1 = 0; 146 | T temp_float_sample2 = 0; 147 | std::uint32_t index = 0; 148 | for (std::uint32_t i = 0; i < new_sample_count; i++) 149 | { 150 | index = std::uint32_t(i * downsample_ratio) * width * channels; 151 | temp_sample1 = GETSAMPLE64(width, src, index) >> (64 - sizeof(T) * 8); 152 | temp_sample2 = GETSAMPLE64(width, src, index + width) >> (64 - sizeof(T) * 8); 153 | temp_float_sample1 = *reinterpret_cast(&temp_sample1); 154 | temp_float_sample2 = *reinterpret_cast(&temp_sample2); 155 | dst->at(i) = LowQualitySample((temp_float_sample1 + temp_float_sample2) / 2 * 156 | LOW_QUALITY_SAMPLE_MAX); 157 | } 158 | } 159 | 160 | template 161 | void Downsampler::floatMultiToMono(LowQualityTrack *dst, const void *src, double downsample_ratio, 162 | std::uint32_t new_sample_count, std::uint32_t width, 163 | std::uint32_t channels) 164 | { 165 | std::uint32_t index = 0; 166 | T collected_sample = 0; 167 | std::uint64_t temp_sample = 0; 168 | for (std::uint32_t i = 0; i < new_sample_count; i++) 169 | { 170 | collected_sample = 0; 171 | index = std::uint32_t(i * downsample_ratio) * width * channels; 172 | for (std::uint32_t k = 0; k < channels; k++) 173 | { 174 | temp_sample = GETSAMPLE64(width, src, index + k * width) >> (64 - sizeof(T) * 8); 175 | collected_sample += *reinterpret_cast(&temp_sample); 176 | } 177 | dst->at(i) = LowQualitySample(collected_sample / channels * LOW_QUALITY_SAMPLE_MAX); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /lib/algorithm/signature_generator.cpp: -------------------------------------------------------------------------------- 1 | #include "algorithm/signature_generator.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "utils/hanning.h" 10 | 11 | SignatureGenerator::SignatureGenerator() 12 | : input_pending_processing_(), sample_processed_(0), max_time_seconds_(3.1), 13 | next_signature_(16000, 0), samples_ring_buffer_(FFT_BUFFER_CHUNK_SIZE, 0), 14 | fft_outputs_(256, {0.0}), spread_ffts_output_(256, {0.0}) 15 | { 16 | } 17 | 18 | void SignatureGenerator::FeedInput(const LowQualityTrack &input) 19 | { 20 | input_pending_processing_.reserve(input_pending_processing_.size() + input.size()); 21 | input_pending_processing_.insert(input_pending_processing_.end(), input.begin(), input.end()); 22 | } 23 | 24 | Signature SignatureGenerator::GetNextSignature() 25 | { 26 | if (input_pending_processing_.size() - sample_processed_ < 128) 27 | { 28 | throw std::runtime_error("Not enough input to generate signature"); 29 | } 30 | 31 | double num_samples = static_cast(next_signature_.num_samples()); 32 | while (input_pending_processing_.size() - sample_processed_ >= 128 && 33 | (num_samples / next_signature_.sample_rate() < max_time_seconds_ || 34 | next_signature_.SumOfPeaksLength() < MAX_PEAKS)) 35 | { 36 | LowQualityTrack input(input_pending_processing_.begin() + sample_processed_, 37 | input_pending_processing_.begin() + sample_processed_ + 128); 38 | 39 | processInput(input); 40 | sample_processed_ += 128; 41 | num_samples = static_cast(next_signature_.num_samples()); 42 | } 43 | 44 | Signature result = std::move(next_signature_); 45 | resetSignatureGenerater(); 46 | return result; // RVO 47 | } 48 | 49 | void SignatureGenerator::processInput(const LowQualityTrack &input) 50 | { 51 | next_signature_.Addnum_samples(input.size()); 52 | for (std::size_t chunk = 0; chunk < input.size(); chunk += 128) 53 | { 54 | LowQualityTrack chunk_input(input.begin() + chunk, input.begin() + chunk + 128); 55 | 56 | doFFT(chunk_input); 57 | doPeakSpreadingAndRecoginzation(); 58 | } 59 | } 60 | 61 | void SignatureGenerator::doFFT(const LowQualityTrack &input) 62 | { 63 | std::copy(input.begin(), input.end(), 64 | samples_ring_buffer_.begin() + samples_ring_buffer_.position()); 65 | 66 | samples_ring_buffer_.position() += input.size(); 67 | samples_ring_buffer_.position() %= FFT_BUFFER_CHUNK_SIZE; 68 | samples_ring_buffer_.num_written() += input.size(); 69 | 70 | std::vector excerpt_from_ring_buffer(FFT_BUFFER_CHUNK_SIZE, 0.0); 71 | 72 | std::copy(samples_ring_buffer_.begin() + samples_ring_buffer_.position(), 73 | samples_ring_buffer_.end(), excerpt_from_ring_buffer.begin()); 74 | 75 | std::copy(samples_ring_buffer_.begin(), 76 | samples_ring_buffer_.begin() + samples_ring_buffer_.position(), 77 | excerpt_from_ring_buffer.begin() + FFT_BUFFER_CHUNK_SIZE - 78 | samples_ring_buffer_.position()); 79 | 80 | for (std::size_t i = 0; i < FFT_BUFFER_CHUNK_SIZE; ++i) 81 | { 82 | excerpt_from_ring_buffer[i] *= HANNIG_MATRIX[i]; 83 | } 84 | 85 | decltype(fft_object_)::FFTOutput real = fft_object_.RFFT(excerpt_from_ring_buffer); 86 | fft_outputs_.Append(real); 87 | } 88 | 89 | void SignatureGenerator::doPeakSpreadingAndRecoginzation() 90 | { 91 | doPeakSpreading(); 92 | 93 | if (spread_ffts_output_.num_written() >= 47) 94 | { 95 | doPeakRecognition(); 96 | } 97 | } 98 | 99 | void SignatureGenerator::doPeakSpreading() 100 | { 101 | auto spread_last_fft = fft_outputs_[fft_outputs_.position() - 1]; 102 | 103 | for (auto position = 0u; position < decltype(fft_object_)::OUTPUT_SIZE; ++position) 104 | { 105 | if (position < decltype(fft_object_)::OUTPUT_SIZE - 2) 106 | { 107 | spread_last_fft[position] = *std::max_element(spread_last_fft.begin() + position, 108 | spread_last_fft.begin() + position + 3); 109 | } 110 | 111 | auto max_value = spread_last_fft[position]; 112 | for (auto former_fft_num : {-1, -3, -6}) 113 | { 114 | auto &former_fft_ouput = 115 | spread_ffts_output_[(spread_ffts_output_.position() + former_fft_num) % 116 | spread_ffts_output_.size()]; 117 | former_fft_ouput[position] = max_value = 118 | std::max(max_value, former_fft_ouput[position]); 119 | } 120 | } 121 | spread_ffts_output_.Append(spread_last_fft); 122 | } 123 | 124 | void SignatureGenerator::doPeakRecognition() 125 | { 126 | const auto &fft_minus_46 = fft_outputs_[(fft_outputs_.position() - 46) % fft_outputs_.size()]; 127 | const auto &fft_minus_49 = 128 | spread_ffts_output_[(spread_ffts_output_.position() - 49) % spread_ffts_output_.size()]; 129 | 130 | auto other_offsets = {-53, -45, 165, 172, 179, 186, 193, 200, 214, 221, 228, 235, 242, 249}; 131 | for (auto bin_position = 10u; bin_position < decltype(fft_object_)::OUTPUT_SIZE; ++bin_position) 132 | { 133 | if (fft_minus_46[bin_position] >= 1.0 / 64.0 && 134 | fft_minus_46[bin_position] >= fft_minus_49[bin_position]) 135 | { 136 | auto max_neighbor_in_fft_minus_49 = 0.0l; 137 | for (auto neighbor_offset : {-10, -7, -4, -3, 1, 2, 5, 8}) 138 | { 139 | max_neighbor_in_fft_minus_49 = std::max( 140 | max_neighbor_in_fft_minus_49, fft_minus_49[bin_position + neighbor_offset]); 141 | } 142 | 143 | if (fft_minus_46[bin_position] > max_neighbor_in_fft_minus_49) 144 | { 145 | auto max_neighbor_in_other_adjacent_ffts = max_neighbor_in_fft_minus_49; 146 | for (auto other_offset : other_offsets) 147 | { 148 | max_neighbor_in_other_adjacent_ffts = std::max( 149 | max_neighbor_in_other_adjacent_ffts, 150 | spread_ffts_output_[(spread_ffts_output_.position() + other_offset) % 151 | spread_ffts_output_.size()][bin_position - 1]); 152 | } 153 | 154 | if (fft_minus_46[bin_position] > max_neighbor_in_other_adjacent_ffts) 155 | { 156 | auto fft_number = spread_ffts_output_.num_written() - 46; 157 | auto peak_magnitude = 158 | std::log(std::max(1.0l / 64, fft_minus_46[bin_position])) * 1477.3 + 6144; 159 | auto peak_magnitude_before = 160 | std::log(std::max(1.0l / 64, fft_minus_46[bin_position - 1])) * 1477.3 + 161 | 6144; 162 | auto peak_magnitude_after = 163 | std::log(std::max(1.0l / 64, fft_minus_46[bin_position + 1])) * 1477.3 + 164 | 6144; 165 | 166 | auto peak_variation_1 = 167 | peak_magnitude * 2 - peak_magnitude_before - peak_magnitude_after; 168 | auto peak_variation_2 = 169 | (peak_magnitude_after - peak_magnitude_before) * 32 / peak_variation_1; 170 | 171 | auto corrected_peak_frequency_bin = bin_position * 64.0 + peak_variation_2; 172 | auto frequency_hz = 173 | corrected_peak_frequency_bin * (16000.0l / 2. / 1024. / 64.); 174 | 175 | auto band = FrequencyBand(); 176 | if (frequency_hz < 250) 177 | continue; 178 | else if (frequency_hz < 520) 179 | band = FrequencyBand::_250_520; 180 | else if (frequency_hz < 1450) 181 | band = FrequencyBand::_520_1450; 182 | else if (frequency_hz < 3500) 183 | band = FrequencyBand::_1450_3500; 184 | else if (frequency_hz <= 5500) 185 | band = FrequencyBand::_3500_5500; 186 | else 187 | continue; 188 | 189 | auto &band_to_sound_peaks = next_signature_.frequency_band_to_peaks(); 190 | if (band_to_sound_peaks.find(band) == band_to_sound_peaks.end()) 191 | { 192 | band_to_sound_peaks[band] = std::list(); 193 | } 194 | 195 | band_to_sound_peaks[band].push_back( 196 | FrequencyPeak(fft_number, static_cast(peak_magnitude), 197 | static_cast(corrected_peak_frequency_bin), 198 | LOW_QUALITY_SAMPLE_RATE)); 199 | } 200 | } 201 | } 202 | } 203 | } 204 | 205 | void SignatureGenerator::resetSignatureGenerater() 206 | { 207 | next_signature_ = Signature(16000, 0); 208 | samples_ring_buffer_ = RingBuffer(FFT_BUFFER_CHUNK_SIZE, 0); 209 | fft_outputs_ = RingBuffer(256, {0.0}); 210 | spread_ffts_output_ = RingBuffer(256, {0.0}); 211 | } 212 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |
4 | 5 |

6 | 7 | 8 | 9 | # vibra 10 | 11 | 12 | 13 |

14 | GitHub stars 15 | 16 | 17 |

18 | 19 | ### Overview 20 | 21 | * **vibra** is a library and CLI tool for music recognition using the **unofficial** Shazam API. 22 | * It analyzes audio files, generates fingerprints, and queries Shazam's database for song identification. 23 | * **Key features**: 24 | * Fast and lightweight, optimized for various platforms, including embedded devices. 25 | * Cross-platform support: Linux, Windows, macOS, **WebAssembly**, and **FFI bindings** for other languages. 26 | * Flexible input processing: native support for WAV files, optional FFmpeg for other formats. 27 | * **Based on Shazam's algorithm**: 28 | * [An Industrial-Strength Audio Search Algorithm](https://www.ee.columbia.edu/~dpwe/papers/Wang03-shazam.pdf) 29 | * [How does Shazam work](https://www.cameronmacleod.com/blog/how-does-shazam-work) 30 | * **Inspired by** [SongRec](https://github.com/marin-m/SongRec/tree/master), adapted to C++ 11. 31 | * **Target platforms**: 32 | * Embedded devices (e.g., Raspberry Pi, Jetson Nano) 33 | * Desktop and server environments for high-performance recognition 34 | * WebAssembly for web-based use 35 | * Additional support for iOS, Android, and other languages via FFI bindings 36 | 37 | ### Live Demo 38 | * You can try the music recognition with the **[WebAssembly version of vibra here](https://bayernmuller.github.io/vibra-live-demo/)** 39 | * The source code for the demo is available at [vibra-live-demo](https://github.com/bayernmuller/vibra-live-demo) 40 | 41 | ### Platform Compatibility and Build Status 42 | 43 | | Platform | Status | Workflows | 44 | |--------|--------|--------| 45 | | **Linux AMD64** | [![linux-amd64-main]][linux-amd64-main] | [build-linux-amd64] | 46 | | **Linux ARM64** | [![linux-arm64-main]][linux-arm64-main] | [build-linux-arm64] | 47 | | **MacOS AMD64** | [![macos-amd64-main]][macos-amd64-main] | [build-macos-amd64] | 48 | | **MacOS ARM64** | [![macos-arm64-main]][macos-arm64-main] | [build-macos-arm64] | 49 | | **Windows AMD64** | [![windows-amd64-main]][windows-amd64-main] | [build-windows-amd64] | 50 | | **WebAssembly** | [![webassembly-main]][webassembly-main] | [build-webassembly] | 51 | | ***Python*** | *Coming soon...* | - | 52 | 53 | ### Building the WebAssembly Version 54 | * Please refer to **[bindings/wasm/README.md](bindings/wasm/README.md)** for instructions on building and running the WebAssembly version of vibra. 55 | 56 | ### Building the Native Version 57 | 58 | #### Prerequisites 59 | * vibra requires CMake for its build process. Please install [CMake](https://cmake.org/) before proceeding. 60 | * The project is developed using the **C++11** standard. 61 | * vibra has the following dependencies: 62 | * [CMake](https://cmake.org/): A cross-platform build system generator. 63 | * [libfftw3](http://www.fftw.org/): A library for computing Fast Fourier Transforms. 64 | * [libcurl](https://curl.se/libcurl/) (CLI tool only): A library for transferring data with URLs. 65 | * If you don't need CLI tool, libcurl is not required. 66 | * You can disable it by setting the `-DLIBRARY_ONLY=ON` option in the CMake command. 67 | * [FFmpeg](https://ffmpeg.org/) (Optional): Provides support for audio formats other than WAV (e.g., MP3, FLAC). 68 | * Install FFmpeg if you need to process audio files in formats other than WAV. 69 | 70 | #### Install dependencies 71 | * **Ubuntu** 72 | * `sudo apt-get update` 73 | * `sudo apt-get install cmake libcurl4-openssl-dev libfftw3-dev` 74 | * `sudo apt-get install ffmpeg` (Optional) 75 | * **Windows** 76 | * Install [CMake](https://cmake.org/download/) 77 | * Install [vcpkg](https://github.com/Microsoft/vcpkg) 78 | * Install dependencies using vcpkg: 79 | * `vcpkg install curl:x64-windows fftw3:x64-windows` 80 | * Add the vcpkg toolchain file to your CMake command (see Build section) 81 | * Install [FFmpeg](https://ffmpeg.org/download.html#build-windows) (Optional) 82 | * **macOS** 83 | * Install [Homebrew](https://brew.sh/) 84 | * `brew install cmake curl fftw` 85 | * `brew install ffmpeg` (Optional) 86 | 87 | 88 | #### Build 89 | * Clone vibra repository 90 | * `git clone https://github.com/bayernmuller/vibra.git` 91 | 92 | * Run the following commands to build vibra: 93 | * `cd vibra` 94 | * `mkdir build && cd build` 95 | * `cmake ..` 96 | * `make` 97 | * `sudo make install` (Optional) 98 | * Installs the libvibra static, shared libraries and the vibra command-line tool. 99 | 100 | #### Usage 101 |
102 | Use --help option to see the help message. 103 | 104 | ``` 105 | vibra {COMMAND} [OPTIONS] 106 | 107 | Options: 108 | 109 | Commands: 110 | -F, --fingerprint Generate a fingerprint 111 | -R, --recognize Recognize a song 112 | -h, --help Display this help menu 113 | Sources: 114 | File sources: 115 | -f, --file File path 116 | Raw PCM sources: 117 | -s, --seconds Chunk seconds 118 | -r, --rate Sample rate 119 | -c, --channels Channels 120 | -b, --bits Bits per sample 121 | ``` 122 | 123 |
124 | 125 | ##### Recognizing a song from a WAV file 126 | ```bash 127 | vibra --recognize --file sample.wav > result.json 128 | 129 | jq .track.title result.json 130 | "Stairway To Heaven" 131 | jq .track.subtitle result.json 132 | "Led Zeppelin" 133 | jq .track.share.href result.json 134 | "https://www.shazam.com/track/5933917/stairway-to-heaven" 135 | ``` 136 | 137 | ##### Recognizing a song from a microphone 138 | * You can use [sox](http://sox.sourceforge.net/) or [FFmpeg](https://ffmpeg.org/) to capture raw PCM data from the **microphone**. 139 | * **sox** 140 | ```bash 141 | sox -d -t raw -b 24 -e signed-integer -r 44100 -c 1 - 2>/dev/null 142 | | vibra --recognize --seconds 5 --rate 44100 --channels 1 --bits 24 > result.json 143 | ``` 144 | 145 | * **FFmpeg** 146 | ```bash 147 | ffmpeg -f avfoundation -i ":2" -f s32le -ar 44100 -ac 1 - 2>/dev/null 148 | | vibra --recognize --seconds 5 --rate 44100 --channels 1 --bits 32 > result.json 149 | # - "avfoundation" can be replaced depending on your system. 150 | # - Make sure to use the correct device index for your system. 151 | ``` 152 | * **output** 153 | ```bash 154 | jq .track.title result.json 155 | "Bound 2" 156 | jq .track.subtitle result.json 157 | "Kanye West" 158 | jq .track.sections[1].text result.json 159 | [ 160 | "B-B-B-Bound to fall in love", 161 | "Bound to fall in love", 162 | "(Uh-huh, honey)", 163 | ... 164 | ] 165 | ``` 166 | 167 | ##### Recognizing non-WAV files 168 | * To decode non-WAV media files, FFmpeg must be installed on your system. 169 | * Vibra will attempt to locate FFmpeg in your system's PATH environment variable. If you prefer, you can explicitly specify the FFmpeg path by setting the `FFMPEG_PATH` environment variable. 170 | ```bash 171 | # Automatically find FFmpeg in PATH 172 | vibra --recognize --file sample.mp3 173 | 174 | # Specify the FFmpeg path 175 | export FFMPEG_PATH=/opt/homebrew/bin/ffmpeg 176 | vibra --recognize --file sample.mp3 177 | # You can use your own FFmpeg which is optimized for your system. 178 | ``` 179 | * You can see the sample shazam result json file in [here](https://gist.github.com/BayernMuller/b92fd43eef4471b7016009196e62e4d2) 180 | 181 | ### FFI Bindings 182 | * vibra provides FFI bindings, allowing other languages to leverage its music recognition functionality. 183 | * After building vibra, the shared library `libvibra.so` will be located in the `build` directory. 184 | * This shared library can be integrated into languages such as Python or Swift using FFI mechanisms. 185 | * For detailed function signatures, please refer to the vibra header file [vibra.h](include/vibra.h). 186 | 187 | 188 | ### Performance comparison 189 |

190 |
191 | lower is better 192 |

193 | 194 | * I compared the performance of vibra with the [SongRec](https://github.com/marin-m/SongRec/tree/master) rust and python version on the Raspberry Pi 4. 195 | * vibra is about 2 times faster than the SongRec! 196 | 197 | ### License 198 | * vibra is licensed under the GPLv3 license. See [LICENSE](LICENSE) for more details. 199 | 200 | 201 | 202 | [linux-amd64-main]: https://github.com/bayernmuller/vibra/actions/workflows/build-linux-amd64.yaml/badge.svg 203 | [build-linux-amd64]: https://github.com/bayernmuller/vibra/tree/main/.github/workflows/build-linux-amd64.yaml 204 | 205 | [linux-arm64-main]: https://github.com/bayernmuller/vibra/actions/workflows/build-linux-arm64.yaml/badge.svg 206 | [build-linux-arm64]: https://github.com/bayernmuller/vibra/tree/main/.github/workflows/build-linux-arm64.yaml 207 | 208 | [macos-amd64-main]: https://github.com/bayernmuller/vibra/actions/workflows/build-macos-amd64.yaml/badge.svg 209 | [build-macos-amd64]: https://github.com/bayernmuller/vibra/tree/main/.github/workflows/build-macos-amd64.yaml 210 | 211 | [macos-arm64-main]: https://github.com/bayernmuller/vibra/actions/workflows/build-macos-arm64.yaml/badge.svg 212 | [build-macos-arm64]: https://github.com/bayernmuller/vibra/tree/main/.github/workflows/build-macos-arm64.yaml 213 | 214 | [windows-amd64-main]: https://github.com/bayernmuller/vibra/actions/workflows/build-windows-amd64.yaml/badge.svg 215 | [build-windows-amd64]: https://github.com/bayernmuller/vibra/tree/main/.github/workflows/build-windows-amd64.yaml 216 | 217 | [webassembly-main]: https://github.com/bayernmuller/vibra/actions/workflows/build-webassembly.yaml/badge.svg 218 | [build-webassembly]: https://github.com/bayernmuller/vibra/tree/main/.github/workflows/build-webassembly.yaml 219 | -------------------------------------------------------------------------------- /bindings/wasm/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vibra", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "vibra", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "bin": { 12 | "vibrajs": "dist/cli.js" 13 | }, 14 | "devDependencies": { 15 | "@types/axios": "^0.14.0", 16 | "@types/node": "^20.11.19", 17 | "@types/uuid": "^9.0.8", 18 | "axios": "^1.6.7", 19 | "typescript": "^5.3.3", 20 | "uuid": "^9.0.1" 21 | } 22 | }, 23 | "node_modules/@types/axios": { 24 | "version": "0.14.0", 25 | "resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.0.tgz", 26 | "integrity": "sha512-KqQnQbdYE54D7oa/UmYVMZKq7CO4l8DEENzOKc4aBRwxCXSlJXGz83flFx5L7AWrOQnmuN3kVsRdt+GZPPjiVQ==", 27 | "deprecated": "This is a stub types definition for axios (https://github.com/mzabriskie/axios). axios provides its own type definitions, so you don't need @types/axios installed!", 28 | "dev": true, 29 | "dependencies": { 30 | "axios": "*" 31 | } 32 | }, 33 | "node_modules/@types/node": { 34 | "version": "20.11.19", 35 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz", 36 | "integrity": "sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==", 37 | "dev": true, 38 | "dependencies": { 39 | "undici-types": "~5.26.4" 40 | } 41 | }, 42 | "node_modules/@types/uuid": { 43 | "version": "9.0.8", 44 | "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", 45 | "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", 46 | "dev": true 47 | }, 48 | "node_modules/asynckit": { 49 | "version": "0.4.0", 50 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 51 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", 52 | "dev": true 53 | }, 54 | "node_modules/axios": { 55 | "version": "1.7.7", 56 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", 57 | "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", 58 | "dev": true, 59 | "dependencies": { 60 | "follow-redirects": "^1.15.6", 61 | "form-data": "^4.0.0", 62 | "proxy-from-env": "^1.1.0" 63 | } 64 | }, 65 | "node_modules/combined-stream": { 66 | "version": "1.0.8", 67 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 68 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 69 | "dev": true, 70 | "dependencies": { 71 | "delayed-stream": "~1.0.0" 72 | }, 73 | "engines": { 74 | "node": ">= 0.8" 75 | } 76 | }, 77 | "node_modules/delayed-stream": { 78 | "version": "1.0.0", 79 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 80 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 81 | "dev": true, 82 | "engines": { 83 | "node": ">=0.4.0" 84 | } 85 | }, 86 | "node_modules/follow-redirects": { 87 | "version": "1.15.9", 88 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", 89 | "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", 90 | "dev": true, 91 | "funding": [ 92 | { 93 | "type": "individual", 94 | "url": "https://github.com/sponsors/RubenVerborgh" 95 | } 96 | ], 97 | "engines": { 98 | "node": ">=4.0" 99 | }, 100 | "peerDependenciesMeta": { 101 | "debug": { 102 | "optional": true 103 | } 104 | } 105 | }, 106 | "node_modules/form-data": { 107 | "version": "4.0.0", 108 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 109 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 110 | "dev": true, 111 | "dependencies": { 112 | "asynckit": "^0.4.0", 113 | "combined-stream": "^1.0.8", 114 | "mime-types": "^2.1.12" 115 | }, 116 | "engines": { 117 | "node": ">= 6" 118 | } 119 | }, 120 | "node_modules/mime-db": { 121 | "version": "1.52.0", 122 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 123 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 124 | "dev": true, 125 | "engines": { 126 | "node": ">= 0.6" 127 | } 128 | }, 129 | "node_modules/mime-types": { 130 | "version": "2.1.35", 131 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 132 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 133 | "dev": true, 134 | "dependencies": { 135 | "mime-db": "1.52.0" 136 | }, 137 | "engines": { 138 | "node": ">= 0.6" 139 | } 140 | }, 141 | "node_modules/proxy-from-env": { 142 | "version": "1.1.0", 143 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 144 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", 145 | "dev": true 146 | }, 147 | "node_modules/typescript": { 148 | "version": "5.3.3", 149 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", 150 | "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", 151 | "dev": true, 152 | "bin": { 153 | "tsc": "bin/tsc", 154 | "tsserver": "bin/tsserver" 155 | }, 156 | "engines": { 157 | "node": ">=14.17" 158 | } 159 | }, 160 | "node_modules/undici-types": { 161 | "version": "5.26.5", 162 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 163 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", 164 | "dev": true 165 | }, 166 | "node_modules/uuid": { 167 | "version": "9.0.1", 168 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", 169 | "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", 170 | "dev": true, 171 | "funding": [ 172 | "https://github.com/sponsors/broofa", 173 | "https://github.com/sponsors/ctavan" 174 | ], 175 | "bin": { 176 | "uuid": "dist/bin/uuid" 177 | } 178 | } 179 | }, 180 | "dependencies": { 181 | "@types/axios": { 182 | "version": "0.14.0", 183 | "resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.0.tgz", 184 | "integrity": "sha512-KqQnQbdYE54D7oa/UmYVMZKq7CO4l8DEENzOKc4aBRwxCXSlJXGz83flFx5L7AWrOQnmuN3kVsRdt+GZPPjiVQ==", 185 | "dev": true, 186 | "requires": { 187 | "axios": "*" 188 | } 189 | }, 190 | "@types/node": { 191 | "version": "20.11.19", 192 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz", 193 | "integrity": "sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==", 194 | "dev": true, 195 | "requires": { 196 | "undici-types": "~5.26.4" 197 | } 198 | }, 199 | "@types/uuid": { 200 | "version": "9.0.8", 201 | "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", 202 | "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", 203 | "dev": true 204 | }, 205 | "asynckit": { 206 | "version": "0.4.0", 207 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 208 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", 209 | "dev": true 210 | }, 211 | "axios": { 212 | "version": "1.7.7", 213 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", 214 | "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", 215 | "dev": true, 216 | "requires": { 217 | "follow-redirects": "^1.15.6", 218 | "form-data": "^4.0.0", 219 | "proxy-from-env": "^1.1.0" 220 | } 221 | }, 222 | "combined-stream": { 223 | "version": "1.0.8", 224 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 225 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 226 | "dev": true, 227 | "requires": { 228 | "delayed-stream": "~1.0.0" 229 | } 230 | }, 231 | "delayed-stream": { 232 | "version": "1.0.0", 233 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 234 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 235 | "dev": true 236 | }, 237 | "follow-redirects": { 238 | "version": "1.15.9", 239 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", 240 | "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", 241 | "dev": true 242 | }, 243 | "form-data": { 244 | "version": "4.0.0", 245 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 246 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 247 | "dev": true, 248 | "requires": { 249 | "asynckit": "^0.4.0", 250 | "combined-stream": "^1.0.8", 251 | "mime-types": "^2.1.12" 252 | } 253 | }, 254 | "mime-db": { 255 | "version": "1.52.0", 256 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 257 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 258 | "dev": true 259 | }, 260 | "mime-types": { 261 | "version": "2.1.35", 262 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 263 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 264 | "dev": true, 265 | "requires": { 266 | "mime-db": "1.52.0" 267 | } 268 | }, 269 | "proxy-from-env": { 270 | "version": "1.1.0", 271 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 272 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", 273 | "dev": true 274 | }, 275 | "typescript": { 276 | "version": "5.3.3", 277 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", 278 | "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", 279 | "dev": true 280 | }, 281 | "undici-types": { 282 | "version": "5.26.5", 283 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 284 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", 285 | "dev": true 286 | }, 287 | "uuid": { 288 | "version": "9.0.1", 289 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", 290 | "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", 291 | "dev": true 292 | } 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /lib/utils/hanning.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_UTILS_HANNING_H_ 2 | #define LIB_UTILS_HANNING_H_ 3 | 4 | constexpr double HANNIG_MATRIX[] = { 5 | 0.0000023508, 0.0000094032, 0.0000211571, 0.0000376123, 0.0000587689, 0.0000846264, 6 | 0.0001151848, 0.0001504437, 0.0001904028, 0.0002350617, 0.0002844199, 0.0003384771, 7 | 0.0003972327, 0.0004606862, 0.0005288369, 0.0006016843, 0.0006792276, 0.0007614661, 8 | 0.0008483991, 0.0009400256, 0.0010363449, 0.0011373561, 0.0012430582, 0.0013534502, 9 | 0.0014685311, 0.0015882997, 0.0017127550, 0.0018418958, 0.0019757209, 0.0021142290, 10 | 0.0022574189, 0.0024052891, 0.0025578382, 0.0027150650, 0.0028769677, 0.0030435451, 11 | 0.0032147954, 0.0033907171, 0.0035713085, 0.0037565679, 0.0039464936, 0.0041410837, 12 | 0.0043403366, 0.0045442502, 0.0047528227, 0.0049660521, 0.0051839364, 0.0054064735, 13 | 0.0056336614, 0.0058654980, 0.0061019809, 0.0063431081, 0.0065888773, 0.0068392860, 14 | 0.0070943321, 0.0073540131, 0.0076183265, 0.0078872699, 0.0081608407, 0.0084390363, 15 | 0.0087218542, 0.0090092917, 0.0093013461, 0.0095980146, 0.0098992944, 0.0102051828, 16 | 0.0105156768, 0.0108307735, 0.0111504700, 0.0114747632, 0.0118036501, 0.0121371277, 17 | 0.0124751927, 0.0128178420, 0.0131650723, 0.0135168805, 0.0138732631, 0.0142342169, 18 | 0.0145997385, 0.0149698243, 0.0153444710, 0.0157236751, 0.0161074328, 0.0164957407, 19 | 0.0168885951, 0.0172859922, 0.0176879285, 0.0180944000, 0.0185054029, 0.0189209335, 20 | 0.0193409877, 0.0197655616, 0.0201946513, 0.0206282527, 0.0210663617, 0.0215089742, 21 | 0.0219560861, 0.0224076931, 0.0228637910, 0.0233243755, 0.0237894423, 0.0242589870, 22 | 0.0247330052, 0.0252114924, 0.0256944441, 0.0261818558, 0.0266737229, 0.0271700408, 23 | 0.0276708049, 0.0281760103, 0.0286856523, 0.0291997263, 0.0297182272, 0.0302411503, 24 | 0.0307684907, 0.0313002433, 0.0318364032, 0.0323769653, 0.0329219246, 0.0334712759, 25 | 0.0340250141, 0.0345831339, 0.0351456301, 0.0357124975, 0.0362837306, 0.0368593242, 26 | 0.0374392727, 0.0380235708, 0.0386122130, 0.0392051936, 0.0398025073, 0.0404041482, 27 | 0.0410101108, 0.0416203894, 0.0422349782, 0.0428538715, 0.0434770634, 0.0441045481, 28 | 0.0447363196, 0.0453723721, 0.0460126996, 0.0466572959, 0.0473061552, 0.0479592712, 29 | 0.0486166378, 0.0492782489, 0.0499440982, 0.0506141795, 0.0512884865, 0.0519670127, 30 | 0.0526497519, 0.0533366976, 0.0540278434, 0.0547231828, 0.0554227092, 0.0561264160, 31 | 0.0568342966, 0.0575463445, 0.0582625528, 0.0589829148, 0.0597074238, 0.0604360730, 32 | 0.0611688555, 0.0619057644, 0.0626467928, 0.0633919337, 0.0641411801, 0.0648945250, 33 | 0.0656519612, 0.0664134817, 0.0671790793, 0.0679487469, 0.0687224770, 0.0695002626, 34 | 0.0702820963, 0.0710679706, 0.0718578783, 0.0726518119, 0.0734497639, 0.0742517269, 35 | 0.0750576933, 0.0758676555, 0.0766816058, 0.0774995368, 0.0783214406, 0.0791473095, 36 | 0.0799771357, 0.0808109116, 0.0816486291, 0.0824902805, 0.0833358578, 0.0841853531, 37 | 0.0850387583, 0.0858960656, 0.0867572667, 0.0876223536, 0.0884913182, 0.0893641523, 38 | 0.0902408477, 0.0911213962, 0.0920057894, 0.0928940190, 0.0937860768, 0.0946819542, 39 | 0.0955816430, 0.0964851345, 0.0973924204, 0.0983034921, 0.0992183410, 0.1001369586, 40 | 0.1010593361, 0.1019854650, 0.1029153364, 0.1038489418, 0.1047862722, 0.1057273189, 41 | 0.1066720730, 0.1076205257, 0.1085726679, 0.1095284909, 0.1104879855, 0.1114511428, 42 | 0.1124179537, 0.1133884090, 0.1143624998, 0.1153402168, 0.1163215508, 0.1173064927, 43 | 0.1182950331, 0.1192871627, 0.1202828722, 0.1212821523, 0.1222849936, 0.1232913866, 44 | 0.1243013218, 0.1253147898, 0.1263317811, 0.1273522860, 0.1283762950, 0.1294037985, 45 | 0.1304347867, 0.1314692500, 0.1325071788, 0.1335485631, 0.1345933932, 0.1356416593, 46 | 0.1366933516, 0.1377484601, 0.1388069749, 0.1398688861, 0.1409341836, 0.1420028576, 47 | 0.1430748978, 0.1441502943, 0.1452290369, 0.1463111155, 0.1473965199, 0.1484852399, 48 | 0.1495772653, 0.1506725857, 0.1517711910, 0.1528730707, 0.1539782145, 0.1550866121, 49 | 0.1561982529, 0.1573131265, 0.1584312225, 0.1595525304, 0.1606770395, 0.1618047394, 50 | 0.1629356193, 0.1640696688, 0.1652068770, 0.1663472334, 0.1674907272, 0.1686373477, 51 | 0.1697870840, 0.1709399254, 0.1720958610, 0.1732548799, 0.1744169713, 0.1755821242, 52 | 0.1767503277, 0.1779215707, 0.1790958423, 0.1802731315, 0.1814534271, 0.1826367180, 53 | 0.1838229932, 0.1850122415, 0.1862044516, 0.1873996124, 0.1885977127, 0.1897987412, 54 | 0.1910026865, 0.1922095374, 0.1934192825, 0.1946319104, 0.1958474098, 0.1970657692, 55 | 0.1982869772, 0.1995110222, 0.2007378927, 0.2019675773, 0.2032000643, 0.2044353422, 56 | 0.2056733994, 0.2069142242, 0.2081578049, 0.2094041298, 0.2106531873, 0.2119049656, 57 | 0.2131594529, 0.2144166374, 0.2156765073, 0.2169390508, 0.2182042559, 0.2194721108, 58 | 0.2207426036, 0.2220157223, 0.2232914549, 0.2245697895, 0.2258507140, 0.2271342163, 59 | 0.2284202845, 0.2297089064, 0.2310000699, 0.2322937629, 0.2335899731, 0.2348886885, 60 | 0.2361898967, 0.2374935856, 0.2387997429, 0.2401083564, 0.2414194136, 0.2427329023, 61 | 0.2440488102, 0.2453671248, 0.2466878338, 0.2480109247, 0.2493363851, 0.2506642026, 62 | 0.2519943646, 0.2533268587, 0.2546616722, 0.2559987928, 0.2573382077, 0.2586799044, 63 | 0.2600238703, 0.2613700928, 0.2627185591, 0.2640692567, 0.2654221727, 0.2667772945, 64 | 0.2681346094, 0.2694941045, 0.2708557672, 0.2722195845, 0.2735855437, 0.2749536319, 65 | 0.2763238362, 0.2776961439, 0.2790705418, 0.2804470173, 0.2818255572, 0.2832061487, 66 | 0.2845887787, 0.2859734343, 0.2873601024, 0.2887487700, 0.2901394241, 0.2915320515, 67 | 0.2929266392, 0.2943231740, 0.2957216428, 0.2971220325, 0.2985243300, 0.2999285219, 68 | 0.3013345951, 0.3027425364, 0.3041523326, 0.3055639704, 0.3069774365, 0.3083927176, 69 | 0.3098098005, 0.3112286717, 0.3126493180, 0.3140717260, 0.3154958823, 0.3169217735, 70 | 0.3183493863, 0.3197787071, 0.3212097227, 0.3226424194, 0.3240767838, 0.3255128025, 71 | 0.3269504619, 0.3283897485, 0.3298306489, 0.3312731493, 0.3327172363, 0.3341628964, 72 | 0.3356101158, 0.3370588810, 0.3385091784, 0.3399609943, 0.3414143150, 0.3428691270, 73 | 0.3443254166, 0.3457831699, 0.3472423734, 0.3487030133, 0.3501650759, 0.3516285474, 74 | 0.3530934141, 0.3545596622, 0.3560272778, 0.3574962473, 0.3589665568, 0.3604381924, 75 | 0.3619111404, 0.3633853868, 0.3648609179, 0.3663377196, 0.3678157783, 0.3692950799, 76 | 0.3707756105, 0.3722573563, 0.3737403032, 0.3752244374, 0.3767097449, 0.3781962117, 77 | 0.3796838239, 0.3811725674, 0.3826624282, 0.3841533924, 0.3856454460, 0.3871385748, 78 | 0.3886327649, 0.3901280022, 0.3916242727, 0.3931215622, 0.3946198567, 0.3961191422, 79 | 0.3976194044, 0.3991206294, 0.4006228029, 0.4021259109, 0.4036299393, 0.4051348738, 80 | 0.4066407004, 0.4081474048, 0.4096549729, 0.4111633906, 0.4126726436, 0.4141827178, 81 | 0.4156935990, 0.4172052728, 0.4187177253, 0.4202309420, 0.4217449088, 0.4232596115, 82 | 0.4247750358, 0.4262911674, 0.4278079921, 0.4293254957, 0.4308436638, 0.4323624822, 83 | 0.4338819366, 0.4354020128, 0.4369226964, 0.4384439731, 0.4399658286, 0.4414882486, 84 | 0.4430112189, 0.4445347250, 0.4460587526, 0.4475832875, 0.4491083153, 0.4506338216, 85 | 0.4521597921, 0.4536862125, 0.4552130684, 0.4567403454, 0.4582680291, 0.4597961053, 86 | 0.4613245596, 0.4628533775, 0.4643825447, 0.4659120468, 0.4674418695, 0.4689719983, 87 | 0.4705024189, 0.4720331168, 0.4735640778, 0.4750952873, 0.4766267310, 0.4781583944, 88 | 0.4796902633, 0.4812223231, 0.4827545595, 0.4842869581, 0.4858195044, 0.4873521841, 89 | 0.4888849827, 0.4904178858, 0.4919508790, 0.4934839479, 0.4950170780, 0.4965502551, 90 | 0.4980834645, 0.4996166920, 0.5011499231, 0.5026831434, 0.5042163384, 0.5057494938, 91 | 0.5072825951, 0.5088156280, 0.5103485780, 0.5118814306, 0.5134141715, 0.5149467863, 92 | 0.5164792606, 0.5180115799, 0.5195437298, 0.5210756959, 0.5226074639, 0.5241390193, 93 | 0.5256703477, 0.5272014347, 0.5287322659, 0.5302628270, 0.5317931035, 0.5333230810, 94 | 0.5348527452, 0.5363820817, 0.5379110760, 0.5394397139, 0.5409679809, 0.5424958627, 95 | 0.5440233449, 0.5455504131, 0.5470770530, 0.5486032503, 0.5501289905, 0.5516542593, 96 | 0.5531790424, 0.5547033255, 0.5562270942, 0.5577503341, 0.5592730311, 0.5607951706, 97 | 0.5623167385, 0.5638377205, 0.5653581021, 0.5668778692, 0.5683970074, 0.5699155024, 98 | 0.5714333401, 0.5729505060, 0.5744669860, 0.5759827657, 0.5774978309, 0.5790121675, 99 | 0.5805257610, 0.5820385974, 0.5835506623, 0.5850619416, 0.5865724210, 0.5880820864, 100 | 0.5895909235, 0.5910989182, 0.5926060563, 0.5941123235, 0.5956177058, 0.5971221890, 101 | 0.5986257590, 0.6001284015, 0.6016301025, 0.6031308479, 0.6046306235, 0.6061294153, 102 | 0.6076272091, 0.6091239908, 0.6106197465, 0.6121144619, 0.6136081231, 0.6151007161, 103 | 0.6165922267, 0.6180826410, 0.6195719449, 0.6210601245, 0.6225471657, 0.6240330546, 104 | 0.6255177772, 0.6270013195, 0.6284836676, 0.6299648076, 0.6314447254, 0.6329234073, 105 | 0.6344008392, 0.6358770074, 0.6373518979, 0.6388254968, 0.6402977903, 0.6417687646, 106 | 0.6432384058, 0.6447067001, 0.6461736337, 0.6476391928, 0.6491033636, 0.6505661323, 107 | 0.6520274853, 0.6534874087, 0.6549458889, 0.6564029120, 0.6578584645, 0.6593125326, 108 | 0.6607651027, 0.6622161610, 0.6636656940, 0.6651136880, 0.6665601294, 0.6680050047, 109 | 0.6694483001, 0.6708900022, 0.6723300973, 0.6737685721, 0.6752054128, 0.6766406060, 110 | 0.6780741383, 0.6795059961, 0.6809361660, 0.6823646344, 0.6837913881, 0.6852164136, 111 | 0.6866396974, 0.6880612262, 0.6894809866, 0.6908989653, 0.6923151490, 0.6937295243, 112 | 0.6951420779, 0.6965527965, 0.6979616669, 0.6993686758, 0.7007738101, 0.7021770564, 113 | 0.7035784016, 0.7049778325, 0.7063753360, 0.7077708989, 0.7091645081, 0.7105561504, 114 | 0.7119458129, 0.7133334824, 0.7147191459, 0.7161027903, 0.7174844027, 0.7188639700, 115 | 0.7202414793, 0.7216169176, 0.7229902721, 0.7243615297, 0.7257306776, 0.7270977029, 116 | 0.7284625927, 0.7298253343, 0.7311859148, 0.7325443214, 0.7339005413, 0.7352545618, 117 | 0.7366063702, 0.7379559537, 0.7393032997, 0.7406483955, 0.7419912284, 0.7433317858, 118 | 0.7446700551, 0.7460060237, 0.7473396791, 0.7486710087, 0.7500000000, 0.7513266405, 119 | 0.7526509177, 0.7539728192, 0.7552923326, 0.7566094454, 0.7579241452, 0.7592364197, 120 | 0.7605462566, 0.7618536435, 0.7631585681, 0.7644610182, 0.7657609816, 0.7670584459, 121 | 0.7683533990, 0.7696458288, 0.7709357230, 0.7722230696, 0.7735078563, 0.7747900713, 122 | 0.7760697023, 0.7773467374, 0.7786211646, 0.7798929718, 0.7811621471, 0.7824286786, 123 | 0.7836925544, 0.7849537626, 0.7862122913, 0.7874681286, 0.7887212629, 0.7899716822, 124 | 0.7912193749, 0.7924643292, 0.7937065334, 0.7949459759, 0.7961826448, 0.7974165288, 125 | 0.7986476160, 0.7998758950, 0.8011013543, 0.8023239822, 0.8035437673, 0.8047606981, 126 | 0.8059747632, 0.8071859511, 0.8083942505, 0.8095996501, 0.8108021384, 0.8120017041, 127 | 0.8131983361, 0.8143920230, 0.8155827536, 0.8167705167, 0.8179553012, 0.8191370958, 128 | 0.8203158896, 0.8214916713, 0.8226644301, 0.8238341547, 0.8250008343, 0.8261644578, 129 | 0.8273250143, 0.8284824929, 0.8296368828, 0.8307881730, 0.8319363527, 0.8330814112, 130 | 0.8342233376, 0.8353621213, 0.8364977515, 0.8376302175, 0.8387595088, 0.8398856146, 131 | 0.8410085244, 0.8421282276, 0.8432447138, 0.8443579723, 0.8454679928, 0.8465747647, 132 | 0.8476782778, 0.8487785216, 0.8498754857, 0.8509691599, 0.8520595339, 0.8531465974, 133 | 0.8542303401, 0.8553107520, 0.8563878228, 0.8574615425, 0.8585319008, 0.8595988878, 134 | 0.8606624934, 0.8617227077, 0.8627795206, 0.8638329222, 0.8648829026, 0.8659294520, 135 | 0.8669725604, 0.8680122182, 0.8690484154, 0.8700811424, 0.8711103895, 0.8721361469, 136 | 0.8731584051, 0.8741771544, 0.8751923852, 0.8762040880, 0.8772122533, 0.8782168716, 137 | 0.8792179335, 0.8802154295, 0.8812093502, 0.8821996864, 0.8831864286, 0.8841695677, 138 | 0.8851490944, 0.8861249994, 0.8870972736, 0.8880659079, 0.8890308931, 0.8899922202, 139 | 0.8909498801, 0.8919038639, 0.8928541624, 0.8938007669, 0.8947436684, 0.8956828581, 140 | 0.8966183271, 0.8975500666, 0.8984780678, 0.8994023221, 0.9003228207, 0.9012395551, 141 | 0.9021525164, 0.9030616963, 0.9039670861, 0.9048686773, 0.9057664615, 0.9066604301, 142 | 0.9075505748, 0.9084368873, 0.9093193592, 0.9101979821, 0.9110727479, 0.9119436483, 143 | 0.9128106750, 0.9136738201, 0.9145330753, 0.9153884325, 0.9162398838, 0.9170874211, 144 | 0.9179310364, 0.9187707219, 0.9196064696, 0.9204382716, 0.9212661201, 0.9220900075, 145 | 0.9229099258, 0.9237258674, 0.9245378246, 0.9253457899, 0.9261497555, 0.9269497139, 146 | 0.9277456577, 0.9285375792, 0.9293254712, 0.9301093261, 0.9308891366, 0.9316648954, 147 | 0.9324365952, 0.9332042287, 0.9339677886, 0.9347272679, 0.9354826594, 0.9362339559, 148 | 0.9369811504, 0.9377242359, 0.9384632054, 0.9391980520, 0.9399287687, 0.9406553486, 149 | 0.9413777850, 0.9420960710, 0.9428101999, 0.9435201650, 0.9442259596, 0.9449275770, 150 | 0.9456250107, 0.9463182541, 0.9470073006, 0.9476921439, 0.9483727774, 0.9490491948, 151 | 0.9497213897, 0.9503893558, 0.9510530868, 0.9517125764, 0.9523678185, 0.9530188069, 152 | 0.9536655355, 0.9543079981, 0.9549461889, 0.9555801016, 0.9562097305, 0.9568350695, 153 | 0.9574561128, 0.9580728546, 0.9586852890, 0.9592934103, 0.9598972128, 0.9604966908, 154 | 0.9610918387, 0.9616826508, 0.9622691216, 0.9628512456, 0.9634290173, 0.9640024313, 155 | 0.9645714822, 0.9651361647, 0.9656964734, 0.9662524030, 0.9668039484, 0.9673511043, 156 | 0.9678938657, 0.9684322273, 0.9689661842, 0.9694957313, 0.9700208637, 0.9705415763, 157 | 0.9710578644, 0.9715697230, 0.9720771473, 0.9725801326, 0.9730786742, 0.9735727673, 158 | 0.9740624073, 0.9745475895, 0.9750283096, 0.9755045628, 0.9759763448, 0.9764436511, 159 | 0.9769064773, 0.9773648190, 0.9778186720, 0.9782680320, 0.9787128947, 0.9791532560, 160 | 0.9795891117, 0.9800204578, 0.9804472901, 0.9808696047, 0.9812873975, 0.9817006648, 161 | 0.9821094025, 0.9825136068, 0.9829132740, 0.9833084002, 0.9836989818, 0.9840850151, 162 | 0.9844664964, 0.9848434222, 0.9852157889, 0.9855835931, 0.9859468312, 0.9863054998, 163 | 0.9866595956, 0.9870091153, 0.9873540555, 0.9876944131, 0.9880301847, 0.9883613673, 164 | 0.9886879578, 0.9890099530, 0.9893273500, 0.9896401457, 0.9899483373, 0.9902519217, 165 | 0.9905508963, 0.9908452580, 0.9911350043, 0.9914201323, 0.9917006395, 0.9919765230, 166 | 0.9922477804, 0.9925144091, 0.9927764066, 0.9930337704, 0.9932864982, 0.9935345874, 167 | 0.9937780359, 0.9940168412, 0.9942510013, 0.9944805137, 0.9947053765, 0.9949255875, 168 | 0.9951411446, 0.9953520458, 0.9955582891, 0.9957598726, 0.9959567943, 0.9961490524, 169 | 0.9963366452, 0.9965195708, 0.9966978276, 0.9968714138, 0.9970403278, 0.9972045681, 170 | 0.9973641330, 0.9975190211, 0.9976692310, 0.9978147612, 0.9979556103, 0.9980917771, 171 | 0.9982232602, 0.9983500584, 0.9984721705, 0.9985895954, 0.9987023320, 0.9988103791, 172 | 0.9989137359, 0.9990124013, 0.9991063743, 0.9991956542, 0.9992802400, 0.9993601310, 173 | 0.9994353264, 0.9995058256, 0.9995716277, 0.9996327323, 0.9996891388, 0.9997408466, 174 | 0.9997878552, 0.9998301643, 0.9998677733, 0.9999006820, 0.9999288900, 0.9999523970, 175 | 0.9999712030, 0.9999853076, 0.9999947107, 0.9999994123, 0.9999994123, 0.9999947107, 176 | 0.9999853076, 0.9999712030, 0.9999523970, 0.9999288900, 0.9999006820, 0.9998677733, 177 | 0.9998301643, 0.9997878552, 0.9997408466, 0.9996891388, 0.9996327323, 0.9995716277, 178 | 0.9995058256, 0.9994353264, 0.9993601310, 0.9992802400, 0.9991956542, 0.9991063743, 179 | 0.9990124013, 0.9989137359, 0.9988103791, 0.9987023320, 0.9985895954, 0.9984721705, 180 | 0.9983500584, 0.9982232602, 0.9980917771, 0.9979556103, 0.9978147612, 0.9976692310, 181 | 0.9975190211, 0.9973641330, 0.9972045681, 0.9970403278, 0.9968714138, 0.9966978276, 182 | 0.9965195708, 0.9963366452, 0.9961490524, 0.9959567943, 0.9957598726, 0.9955582891, 183 | 0.9953520458, 0.9951411446, 0.9949255875, 0.9947053765, 0.9944805137, 0.9942510013, 184 | 0.9940168412, 0.9937780359, 0.9935345874, 0.9932864982, 0.9930337704, 0.9927764066, 185 | 0.9925144091, 0.9922477804, 0.9919765230, 0.9917006395, 0.9914201323, 0.9911350043, 186 | 0.9908452580, 0.9905508963, 0.9902519217, 0.9899483373, 0.9896401457, 0.9893273500, 187 | 0.9890099530, 0.9886879578, 0.9883613673, 0.9880301847, 0.9876944131, 0.9873540555, 188 | 0.9870091153, 0.9866595956, 0.9863054998, 0.9859468312, 0.9855835931, 0.9852157889, 189 | 0.9848434222, 0.9844664964, 0.9840850151, 0.9836989818, 0.9833084002, 0.9829132740, 190 | 0.9825136068, 0.9821094025, 0.9817006648, 0.9812873975, 0.9808696047, 0.9804472901, 191 | 0.9800204578, 0.9795891117, 0.9791532560, 0.9787128947, 0.9782680320, 0.9778186720, 192 | 0.9773648190, 0.9769064773, 0.9764436511, 0.9759763448, 0.9755045628, 0.9750283096, 193 | 0.9745475895, 0.9740624073, 0.9735727673, 0.9730786742, 0.9725801326, 0.9720771473, 194 | 0.9715697230, 0.9710578644, 0.9705415763, 0.9700208637, 0.9694957313, 0.9689661842, 195 | 0.9684322273, 0.9678938657, 0.9673511043, 0.9668039484, 0.9662524030, 0.9656964734, 196 | 0.9651361647, 0.9645714822, 0.9640024313, 0.9634290173, 0.9628512456, 0.9622691216, 197 | 0.9616826508, 0.9610918387, 0.9604966908, 0.9598972128, 0.9592934103, 0.9586852890, 198 | 0.9580728546, 0.9574561128, 0.9568350695, 0.9562097305, 0.9555801016, 0.9549461889, 199 | 0.9543079981, 0.9536655355, 0.9530188069, 0.9523678185, 0.9517125764, 0.9510530868, 200 | 0.9503893558, 0.9497213897, 0.9490491948, 0.9483727774, 0.9476921439, 0.9470073006, 201 | 0.9463182541, 0.9456250107, 0.9449275770, 0.9442259596, 0.9435201650, 0.9428101999, 202 | 0.9420960710, 0.9413777850, 0.9406553486, 0.9399287687, 0.9391980520, 0.9384632054, 203 | 0.9377242359, 0.9369811504, 0.9362339559, 0.9354826594, 0.9347272679, 0.9339677886, 204 | 0.9332042287, 0.9324365952, 0.9316648954, 0.9308891366, 0.9301093261, 0.9293254712, 205 | 0.9285375792, 0.9277456577, 0.9269497139, 0.9261497555, 0.9253457899, 0.9245378246, 206 | 0.9237258674, 0.9229099258, 0.9220900075, 0.9212661201, 0.9204382716, 0.9196064696, 207 | 0.9187707219, 0.9179310364, 0.9170874211, 0.9162398838, 0.9153884325, 0.9145330753, 208 | 0.9136738201, 0.9128106750, 0.9119436483, 0.9110727479, 0.9101979821, 0.9093193592, 209 | 0.9084368873, 0.9075505748, 0.9066604301, 0.9057664615, 0.9048686773, 0.9039670861, 210 | 0.9030616963, 0.9021525164, 0.9012395551, 0.9003228207, 0.8994023221, 0.8984780678, 211 | 0.8975500666, 0.8966183271, 0.8956828581, 0.8947436684, 0.8938007669, 0.8928541624, 212 | 0.8919038639, 0.8909498801, 0.8899922202, 0.8890308931, 0.8880659079, 0.8870972736, 213 | 0.8861249994, 0.8851490944, 0.8841695677, 0.8831864286, 0.8821996864, 0.8812093502, 214 | 0.8802154295, 0.8792179335, 0.8782168716, 0.8772122533, 0.8762040880, 0.8751923852, 215 | 0.8741771544, 0.8731584051, 0.8721361469, 0.8711103895, 0.8700811424, 0.8690484154, 216 | 0.8680122182, 0.8669725604, 0.8659294520, 0.8648829026, 0.8638329222, 0.8627795206, 217 | 0.8617227077, 0.8606624934, 0.8595988878, 0.8585319008, 0.8574615425, 0.8563878228, 218 | 0.8553107520, 0.8542303401, 0.8531465974, 0.8520595339, 0.8509691599, 0.8498754857, 219 | 0.8487785216, 0.8476782778, 0.8465747647, 0.8454679928, 0.8443579723, 0.8432447138, 220 | 0.8421282276, 0.8410085244, 0.8398856146, 0.8387595088, 0.8376302175, 0.8364977515, 221 | 0.8353621213, 0.8342233376, 0.8330814112, 0.8319363527, 0.8307881730, 0.8296368828, 222 | 0.8284824929, 0.8273250143, 0.8261644578, 0.8250008343, 0.8238341547, 0.8226644301, 223 | 0.8214916713, 0.8203158896, 0.8191370958, 0.8179553012, 0.8167705167, 0.8155827536, 224 | 0.8143920230, 0.8131983361, 0.8120017041, 0.8108021384, 0.8095996501, 0.8083942505, 225 | 0.8071859511, 0.8059747632, 0.8047606981, 0.8035437673, 0.8023239822, 0.8011013543, 226 | 0.7998758950, 0.7986476160, 0.7974165288, 0.7961826448, 0.7949459759, 0.7937065334, 227 | 0.7924643292, 0.7912193749, 0.7899716822, 0.7887212629, 0.7874681286, 0.7862122913, 228 | 0.7849537626, 0.7836925544, 0.7824286786, 0.7811621471, 0.7798929718, 0.7786211646, 229 | 0.7773467374, 0.7760697023, 0.7747900713, 0.7735078563, 0.7722230696, 0.7709357230, 230 | 0.7696458288, 0.7683533990, 0.7670584459, 0.7657609816, 0.7644610182, 0.7631585681, 231 | 0.7618536435, 0.7605462566, 0.7592364197, 0.7579241452, 0.7566094454, 0.7552923326, 232 | 0.7539728192, 0.7526509177, 0.7513266405, 0.7500000000, 0.7486710087, 0.7473396791, 233 | 0.7460060237, 0.7446700551, 0.7433317858, 0.7419912284, 0.7406483955, 0.7393032997, 234 | 0.7379559537, 0.7366063702, 0.7352545618, 0.7339005413, 0.7325443214, 0.7311859148, 235 | 0.7298253343, 0.7284625927, 0.7270977029, 0.7257306776, 0.7243615297, 0.7229902721, 236 | 0.7216169176, 0.7202414793, 0.7188639700, 0.7174844027, 0.7161027903, 0.7147191459, 237 | 0.7133334824, 0.7119458129, 0.7105561504, 0.7091645081, 0.7077708989, 0.7063753360, 238 | 0.7049778325, 0.7035784016, 0.7021770564, 0.7007738101, 0.6993686758, 0.6979616669, 239 | 0.6965527965, 0.6951420779, 0.6937295243, 0.6923151490, 0.6908989653, 0.6894809866, 240 | 0.6880612262, 0.6866396974, 0.6852164136, 0.6837913881, 0.6823646344, 0.6809361660, 241 | 0.6795059961, 0.6780741383, 0.6766406060, 0.6752054128, 0.6737685721, 0.6723300973, 242 | 0.6708900022, 0.6694483001, 0.6680050047, 0.6665601294, 0.6651136880, 0.6636656940, 243 | 0.6622161610, 0.6607651027, 0.6593125326, 0.6578584645, 0.6564029120, 0.6549458889, 244 | 0.6534874087, 0.6520274853, 0.6505661323, 0.6491033636, 0.6476391928, 0.6461736337, 245 | 0.6447067001, 0.6432384058, 0.6417687646, 0.6402977903, 0.6388254968, 0.6373518979, 246 | 0.6358770074, 0.6344008392, 0.6329234073, 0.6314447254, 0.6299648076, 0.6284836676, 247 | 0.6270013195, 0.6255177772, 0.6240330546, 0.6225471657, 0.6210601245, 0.6195719449, 248 | 0.6180826410, 0.6165922267, 0.6151007161, 0.6136081231, 0.6121144619, 0.6106197465, 249 | 0.6091239908, 0.6076272091, 0.6061294153, 0.6046306235, 0.6031308479, 0.6016301025, 250 | 0.6001284015, 0.5986257590, 0.5971221890, 0.5956177058, 0.5941123235, 0.5926060563, 251 | 0.5910989182, 0.5895909235, 0.5880820864, 0.5865724210, 0.5850619416, 0.5835506623, 252 | 0.5820385974, 0.5805257610, 0.5790121675, 0.5774978309, 0.5759827657, 0.5744669860, 253 | 0.5729505060, 0.5714333401, 0.5699155024, 0.5683970074, 0.5668778692, 0.5653581021, 254 | 0.5638377205, 0.5623167385, 0.5607951706, 0.5592730311, 0.5577503341, 0.5562270942, 255 | 0.5547033255, 0.5531790424, 0.5516542593, 0.5501289905, 0.5486032503, 0.5470770530, 256 | 0.5455504131, 0.5440233449, 0.5424958627, 0.5409679809, 0.5394397139, 0.5379110760, 257 | 0.5363820817, 0.5348527452, 0.5333230810, 0.5317931035, 0.5302628270, 0.5287322659, 258 | 0.5272014347, 0.5256703477, 0.5241390193, 0.5226074639, 0.5210756959, 0.5195437298, 259 | 0.5180115799, 0.5164792606, 0.5149467863, 0.5134141715, 0.5118814306, 0.5103485780, 260 | 0.5088156280, 0.5072825951, 0.5057494938, 0.5042163384, 0.5026831434, 0.5011499231, 261 | 0.4996166920, 0.4980834645, 0.4965502551, 0.4950170780, 0.4934839479, 0.4919508790, 262 | 0.4904178858, 0.4888849827, 0.4873521841, 0.4858195044, 0.4842869581, 0.4827545595, 263 | 0.4812223231, 0.4796902633, 0.4781583944, 0.4766267310, 0.4750952873, 0.4735640778, 264 | 0.4720331168, 0.4705024189, 0.4689719983, 0.4674418695, 0.4659120468, 0.4643825447, 265 | 0.4628533775, 0.4613245596, 0.4597961053, 0.4582680291, 0.4567403454, 0.4552130684, 266 | 0.4536862125, 0.4521597921, 0.4506338216, 0.4491083153, 0.4475832875, 0.4460587526, 267 | 0.4445347250, 0.4430112189, 0.4414882486, 0.4399658286, 0.4384439731, 0.4369226964, 268 | 0.4354020128, 0.4338819366, 0.4323624822, 0.4308436638, 0.4293254957, 0.4278079921, 269 | 0.4262911674, 0.4247750358, 0.4232596115, 0.4217449088, 0.4202309420, 0.4187177253, 270 | 0.4172052728, 0.4156935990, 0.4141827178, 0.4126726436, 0.4111633906, 0.4096549729, 271 | 0.4081474048, 0.4066407004, 0.4051348738, 0.4036299393, 0.4021259109, 0.4006228029, 272 | 0.3991206294, 0.3976194044, 0.3961191422, 0.3946198567, 0.3931215622, 0.3916242727, 273 | 0.3901280022, 0.3886327649, 0.3871385748, 0.3856454460, 0.3841533924, 0.3826624282, 274 | 0.3811725674, 0.3796838239, 0.3781962117, 0.3767097449, 0.3752244374, 0.3737403032, 275 | 0.3722573563, 0.3707756105, 0.3692950799, 0.3678157783, 0.3663377196, 0.3648609179, 276 | 0.3633853868, 0.3619111404, 0.3604381924, 0.3589665568, 0.3574962473, 0.3560272778, 277 | 0.3545596622, 0.3530934141, 0.3516285474, 0.3501650759, 0.3487030133, 0.3472423734, 278 | 0.3457831699, 0.3443254166, 0.3428691270, 0.3414143150, 0.3399609943, 0.3385091784, 279 | 0.3370588810, 0.3356101158, 0.3341628964, 0.3327172363, 0.3312731493, 0.3298306489, 280 | 0.3283897485, 0.3269504619, 0.3255128025, 0.3240767838, 0.3226424194, 0.3212097227, 281 | 0.3197787071, 0.3183493863, 0.3169217735, 0.3154958823, 0.3140717260, 0.3126493180, 282 | 0.3112286717, 0.3098098005, 0.3083927176, 0.3069774365, 0.3055639704, 0.3041523326, 283 | 0.3027425364, 0.3013345951, 0.2999285219, 0.2985243300, 0.2971220325, 0.2957216428, 284 | 0.2943231740, 0.2929266392, 0.2915320515, 0.2901394241, 0.2887487700, 0.2873601024, 285 | 0.2859734343, 0.2845887787, 0.2832061487, 0.2818255572, 0.2804470173, 0.2790705418, 286 | 0.2776961439, 0.2763238362, 0.2749536319, 0.2735855437, 0.2722195845, 0.2708557672, 287 | 0.2694941045, 0.2681346094, 0.2667772945, 0.2654221727, 0.2640692567, 0.2627185591, 288 | 0.2613700928, 0.2600238703, 0.2586799044, 0.2573382077, 0.2559987928, 0.2546616722, 289 | 0.2533268587, 0.2519943646, 0.2506642026, 0.2493363851, 0.2480109247, 0.2466878338, 290 | 0.2453671248, 0.2440488102, 0.2427329023, 0.2414194136, 0.2401083564, 0.2387997429, 291 | 0.2374935856, 0.2361898967, 0.2348886885, 0.2335899731, 0.2322937629, 0.2310000699, 292 | 0.2297089064, 0.2284202845, 0.2271342163, 0.2258507140, 0.2245697895, 0.2232914549, 293 | 0.2220157223, 0.2207426036, 0.2194721108, 0.2182042559, 0.2169390508, 0.2156765073, 294 | 0.2144166374, 0.2131594529, 0.2119049656, 0.2106531873, 0.2094041298, 0.2081578049, 295 | 0.2069142242, 0.2056733994, 0.2044353422, 0.2032000643, 0.2019675773, 0.2007378927, 296 | 0.1995110222, 0.1982869772, 0.1970657692, 0.1958474098, 0.1946319104, 0.1934192825, 297 | 0.1922095374, 0.1910026865, 0.1897987412, 0.1885977127, 0.1873996124, 0.1862044516, 298 | 0.1850122415, 0.1838229932, 0.1826367180, 0.1814534271, 0.1802731315, 0.1790958423, 299 | 0.1779215707, 0.1767503277, 0.1755821242, 0.1744169713, 0.1732548799, 0.1720958610, 300 | 0.1709399254, 0.1697870840, 0.1686373477, 0.1674907272, 0.1663472334, 0.1652068770, 301 | 0.1640696688, 0.1629356193, 0.1618047394, 0.1606770395, 0.1595525304, 0.1584312225, 302 | 0.1573131265, 0.1561982529, 0.1550866121, 0.1539782145, 0.1528730707, 0.1517711910, 303 | 0.1506725857, 0.1495772653, 0.1484852399, 0.1473965199, 0.1463111155, 0.1452290369, 304 | 0.1441502943, 0.1430748978, 0.1420028576, 0.1409341836, 0.1398688861, 0.1388069749, 305 | 0.1377484601, 0.1366933516, 0.1356416593, 0.1345933932, 0.1335485631, 0.1325071788, 306 | 0.1314692500, 0.1304347867, 0.1294037985, 0.1283762950, 0.1273522860, 0.1263317811, 307 | 0.1253147898, 0.1243013218, 0.1232913866, 0.1222849936, 0.1212821523, 0.1202828722, 308 | 0.1192871627, 0.1182950331, 0.1173064927, 0.1163215508, 0.1153402168, 0.1143624998, 309 | 0.1133884090, 0.1124179537, 0.1114511428, 0.1104879855, 0.1095284909, 0.1085726679, 310 | 0.1076205257, 0.1066720730, 0.1057273189, 0.1047862722, 0.1038489418, 0.1029153364, 311 | 0.1019854650, 0.1010593361, 0.1001369586, 0.0992183410, 0.0983034921, 0.0973924204, 312 | 0.0964851345, 0.0955816430, 0.0946819542, 0.0937860768, 0.0928940190, 0.0920057894, 313 | 0.0911213962, 0.0902408477, 0.0893641523, 0.0884913182, 0.0876223536, 0.0867572667, 314 | 0.0858960656, 0.0850387583, 0.0841853531, 0.0833358578, 0.0824902805, 0.0816486291, 315 | 0.0808109116, 0.0799771357, 0.0791473095, 0.0783214406, 0.0774995368, 0.0766816058, 316 | 0.0758676555, 0.0750576933, 0.0742517269, 0.0734497639, 0.0726518119, 0.0718578783, 317 | 0.0710679706, 0.0702820963, 0.0695002626, 0.0687224770, 0.0679487469, 0.0671790793, 318 | 0.0664134817, 0.0656519612, 0.0648945250, 0.0641411801, 0.0633919337, 0.0626467928, 319 | 0.0619057644, 0.0611688555, 0.0604360730, 0.0597074238, 0.0589829148, 0.0582625528, 320 | 0.0575463445, 0.0568342966, 0.0561264160, 0.0554227092, 0.0547231828, 0.0540278434, 321 | 0.0533366976, 0.0526497519, 0.0519670127, 0.0512884865, 0.0506141795, 0.0499440982, 322 | 0.0492782489, 0.0486166378, 0.0479592712, 0.0473061552, 0.0466572959, 0.0460126996, 323 | 0.0453723721, 0.0447363196, 0.0441045481, 0.0434770634, 0.0428538715, 0.0422349782, 324 | 0.0416203894, 0.0410101108, 0.0404041482, 0.0398025073, 0.0392051936, 0.0386122130, 325 | 0.0380235708, 0.0374392727, 0.0368593242, 0.0362837306, 0.0357124975, 0.0351456301, 326 | 0.0345831339, 0.0340250141, 0.0334712759, 0.0329219246, 0.0323769653, 0.0318364032, 327 | 0.0313002433, 0.0307684907, 0.0302411503, 0.0297182272, 0.0291997263, 0.0286856523, 328 | 0.0281760103, 0.0276708049, 0.0271700408, 0.0266737229, 0.0261818558, 0.0256944441, 329 | 0.0252114924, 0.0247330052, 0.0242589870, 0.0237894423, 0.0233243755, 0.0228637910, 330 | 0.0224076931, 0.0219560861, 0.0215089742, 0.0210663617, 0.0206282527, 0.0201946513, 331 | 0.0197655616, 0.0193409877, 0.0189209335, 0.0185054029, 0.0180944000, 0.0176879285, 332 | 0.0172859922, 0.0168885951, 0.0164957407, 0.0161074328, 0.0157236751, 0.0153444710, 333 | 0.0149698243, 0.0145997385, 0.0142342169, 0.0138732631, 0.0135168805, 0.0131650723, 334 | 0.0128178420, 0.0124751927, 0.0121371277, 0.0118036501, 0.0114747632, 0.0111504700, 335 | 0.0108307735, 0.0105156768, 0.0102051828, 0.0098992944, 0.0095980146, 0.0093013461, 336 | 0.0090092917, 0.0087218542, 0.0084390363, 0.0081608407, 0.0078872699, 0.0076183265, 337 | 0.0073540131, 0.0070943321, 0.0068392860, 0.0065888773, 0.0063431081, 0.0061019809, 338 | 0.0058654980, 0.0056336614, 0.0054064735, 0.0051839364, 0.0049660521, 0.0047528227, 339 | 0.0045442502, 0.0043403366, 0.0041410837, 0.0039464936, 0.0037565679, 0.0035713085, 340 | 0.0033907171, 0.0032147954, 0.0030435451, 0.0028769677, 0.0027150650, 0.0025578382, 341 | 0.0024052891, 0.0022574189, 0.0021142290, 0.0019757209, 0.0018418958, 0.0017127550, 342 | 0.0015882997, 0.0014685311, 0.0013534502, 0.0012430582, 0.0011373561, 0.0010363449, 343 | 0.0009400256, 0.0008483991, 0.0007614661, 0.0006792276, 0.0006016843, 0.0005288369, 344 | 0.0004606862, 0.0003972327, 0.0003384771, 0.0002844199, 0.0002350617, 0.0001904028, 345 | 0.0001504437, 0.0001151848, 0.0000846264, 0.0000587689, 0.0000376123, 0.0000211571, 346 | 0.0000094032, 0.0000023508}; 347 | 348 | #endif // LIB_UTILS_HANNING_H_ 349 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . --------------------------------------------------------------------------------