diff --git a/.clang-format b/.clang-format index e5534fd..37ca78b 100644 --- a/.clang-format +++ b/.clang-format @@ -4,9 +4,10 @@ Language: Cpp # Style BasedOnStyle: Microsoft -# No indent for extern block and namespace +# No indent for extern block, namespace, and access specifiers in class IndentExternBlock: NoIndent NamespaceIndentation: None +AccessModifierOffset: -4 # Header files order IncludeIsMainRegex: ".*" # 1. main file diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 326b394..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "cli/args"] - path = cli/args - url = https://github.com/Taywee/args diff --git a/README.md b/README.md index 82cbbcc..668b035 100644 --- a/README.md +++ b/README.md @@ -112,8 +112,8 @@ #### Build -* Clone repository **recursively** to include submodules. - * `git clone --recursive https://github.com/bayernmuller/vibra.git` +* Clone vibra repository + * `git clone https://github.com/bayernmuller/vibra.git` * Run the following commands to build vibra: * `cd vibra` diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt index 2f67768..17c6f95 100644 --- a/cli/CMakeLists.txt +++ b/cli/CMakeLists.txt @@ -1,9 +1,25 @@ +# Fetch the args header-only library +include(FetchContent) +FetchContent_Declare( + args + GIT_REPOSITORY https://github.com/Taywee/args.git + GIT_TAG master +) +FetchContent_Populate(args) + # Add the executable for the CLI add_executable(vibra main.cpp cli.cpp + communication/shazam.cpp ) +# Add the args include directory to the target +target_include_directories(vibra PRIVATE ${args_SOURCE_DIR}) + +# Add the include directory to the target +target_include_directories(vibra PRIVATE ${CMAKE_SOURCE_DIR}/cli) + # Find libcurl find_library(CURL_LIBRARY NAMES curl) diff --git a/cli/args b/cli/args deleted file mode 160000 index b7d6723..0000000 --- a/cli/args +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b7d67237e8bdaa517d7fd6e4e84e1f6efa24f8c5 diff --git a/cli/cli.cpp b/cli/cli.cpp index e5ce501..6d42ab8 100644 --- a/cli/cli.cpp +++ b/cli/cli.cpp @@ -1,17 +1,9 @@ #include "../cli/cli.h" -#include #include #include #include -#include "args/args.hxx" - -std::size_t writeCallback(void *contents, size_t size, size_t nmemb, void *userp) -{ - std::string *buffer = reinterpret_cast(userp); - std::size_t realsize = size * nmemb; - buffer->append(reinterpret_cast(contents), realsize); - return realsize; -} +#include +#include "communication/shazam.h" int CLI::Run(int argc, char **argv) { @@ -93,7 +85,7 @@ int CLI::Run(int argc, char **argv) } else if (recognize) { - std::cout << getMetadataFromShazam(fingerprint) << std::endl; + std::cout << Shazam::Recognize(fingerprint) << std::endl; } return 0; } @@ -122,49 +114,3 @@ Fingerprint *CLI::getFingerprintFromStdin(int chunk_seconds, int sample_rate, in return vibra_get_fingerprint_from_float_pcm(buffer.data(), bytes, sample_rate, bits_per_sample, channels); } - -std::string CLI::getMetadataFromShazam(const Fingerprint *fingerprint) -{ - auto content = vibra_get_shazam_request_json(fingerprint); - auto user_agent = vibra_get_shazam_random_user_agent(); - std::string url = vibra_get_shazam_host(); - - CURL *curl = curl_easy_init(); - std::string read_buffer; - - if (curl) - { - struct curl_slist *headers = nullptr; - headers = curl_slist_append(headers, "Accept-Encoding: gzip, deflate, br"); - headers = curl_slist_append(headers, "Accept: */*"); - headers = curl_slist_append(headers, "Connection: keep-alive"); - headers = curl_slist_append(headers, "Content-Type: application/json"); - headers = curl_slist_append(headers, "Content-Language: en_US"); - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_POST, 1L); - curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, content); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &read_buffer); - - curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "gzip, deflate, br"); - curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - - CURLcode res = curl_easy_perform(curl); - if (res != CURLE_OK) - { - std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl; - } - - std::int64_t http_code = 0; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - if (http_code != 200) - { - std::cerr << "HTTP code: " << http_code << std::endl; - } - curl_slist_free_all(headers); - curl_easy_cleanup(curl); - } - return read_buffer; -} diff --git a/cli/cli.h b/cli/cli.h index b200e96..928cd54 100644 --- a/cli/cli.h +++ b/cli/cli.h @@ -6,14 +6,13 @@ class CLI { - public: +public: int Run(int argc, char **argv); - private: +private: Fingerprint *getFingerprintFromMusicFile(const std::string &music_file); Fingerprint *getFingerprintFromStdin(int chunk_seconds, int sample_rate, int channels, int bits_per_sample, bool is_signed); - std::string getMetadataFromShazam(const Fingerprint *fingerprint); }; #endif // CLI_CLI_H_ diff --git a/cli/communication/shazam.cpp b/cli/communication/shazam.cpp new file mode 100644 index 0000000..249befa --- /dev/null +++ b/cli/communication/shazam.cpp @@ -0,0 +1,121 @@ +#include "communication/shazam.h" +#include +#include +#include +#include +#include "communication/timezones.h" +#include "communication/user_agents.h" +#include "utils/uuid4.h" +#include "../../include/vibra.h" + +// static variables initialization +constexpr char Shazam::HOST[]; + +std::size_t writeCallback(void *contents, size_t size, size_t nmemb, void *userp) +{ + std::string *buffer = reinterpret_cast(userp); + std::size_t realsize = size * nmemb; + buffer->append(reinterpret_cast(contents), realsize); + return realsize; +} + +std::string Shazam::Recognize(const Fingerprint *fingerprint) +{ + auto content = getRequestContent(fingerprint->uri, fingerprint->sample_ms); + auto user_agent = getUserAgent(); + std::string url = getShazamHost(); + + CURL *curl = curl_easy_init(); + std::string read_buffer; + + if (curl) + { + struct curl_slist *headers = nullptr; + headers = curl_slist_append(headers, "Accept-Encoding: gzip, deflate, br"); + headers = curl_slist_append(headers, "Accept: */*"); + headers = curl_slist_append(headers, "Connection: keep-alive"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + headers = curl_slist_append(headers, "Content-Language: en_US"); + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_POST, 1L); + curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent.c_str()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, content.c_str()); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &read_buffer); + + curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "gzip, deflate, br"); + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + + CURLcode res = curl_easy_perform(curl); + if (res != CURLE_OK) + { + std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl; + } + + std::int64_t http_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); + if (http_code != 200) + { + std::cerr << "HTTP code: " << http_code << std::endl; + } + curl_slist_free_all(headers); + curl_easy_cleanup(curl); + } + return read_buffer; +} + +std::string Shazam::getShazamHost() +{ + std::string host = HOST + uuid4::generate() + "/" + uuid4::generate(); + host += "?sync=true&" + "webv3=true&" + "sampling=true&" + "connected=&" + "shazamapiversion=v3&" + "sharehub=true&" + "video=v3"; + return host; +} + +std::string Shazam::getRequestContent(const std::string &uri, unsigned int sample_ms) +{ + std::mt19937 gen(std::random_device{}()); + std::uniform_int_distribution<> dis_float(0.0, 1.0); + + auto timezone = getTimezone(); + double fuzz = dis_float(gen) * 15.3 - 7.65; + + std::stringstream json_buf; + json_buf << "{"; + json_buf << "\"geolocation\":{"; + json_buf << "\"altitude\":" << dis_float(gen) * 400 + 100 + fuzz << ","; + json_buf << "\"latitude\":" << dis_float(gen) * 180 - 90 + fuzz << ","; + json_buf << "\"longitude\":" << dis_float(gen) * 360 - 180 + fuzz; + json_buf << "},"; + json_buf << "\"signature\":{"; + json_buf << "\"samplems\":" << sample_ms << ","; + json_buf << "\"timestamp\":" << time(nullptr) * 1000ULL << ","; + json_buf << "\"uri\":\"" << uri << "\""; + json_buf << "},"; + json_buf << "\"timestamp\":" << time(nullptr) * 1000ULL << ","; + json_buf << "\"timezone\":" + << "\"" << timezone << "\""; + json_buf << "}"; + std::string content = json_buf.str(); + return content; +} + +std::string Shazam::getUserAgent() +{ + std::mt19937 gen(std::random_device{}()); + std::uniform_int_distribution<> dis_useragent(0, USER_AGENTS_SIZE - 1); + return USER_AGENTS[dis_useragent(gen)]; +} + +std::string Shazam::getTimezone() +{ + std::mt19937 gen(std::random_device{}()); + std::uniform_int_distribution<> dis_timezone(0, EUROPE_TIMEZONES_SIZE - 1); + return EUROPE_TIMEZONES[dis_timezone(gen)]; +} diff --git a/cli/communication/shazam.h b/cli/communication/shazam.h new file mode 100644 index 0000000..9fe8e51 --- /dev/null +++ b/cli/communication/shazam.h @@ -0,0 +1,24 @@ +#ifndef CLI_COMMUNICATION_SHAZAM_H_ +#define CLI_COMMUNICATION_SHAZAM_H_ + +#include + +// forward declaration +struct Fingerprint; +// + +class Shazam +{ + static constexpr char HOST[] = "https://amp.shazam.com/discovery/v5/fr/FR/android/-/tag/"; + +public: + static std::string Recognize(const Fingerprint *fingerprint); + +private: + static std::string getShazamHost(); + static std::string getUserAgent(); + static std::string getRequestContent(const std::string &uri, unsigned int sample_ms); + static std::string getTimezone(); +}; + +#endif // CLI_COMMUNICATION_SHAZAM_H_ diff --git a/lib/communication/timezones.h b/cli/communication/timezones.h similarity index 92% rename from lib/communication/timezones.h rename to cli/communication/timezones.h index 608dbf0..aa30c3d 100644 --- a/lib/communication/timezones.h +++ b/cli/communication/timezones.h @@ -1,5 +1,5 @@ -#ifndef LIB_COMMUNICATION_TIMEZONES_H_ -#define LIB_COMMUNICATION_TIMEZONES_H_ +#ifndef CLI_COMMUNICATION_TIMEZONES_H_ +#define CLI_COMMUNICATION_TIMEZONES_H_ static constexpr const char *EUROPE_TIMEZONES[] = { "Europe/Amsterdam", "Europe/Andorra", "Europe/Astrakhan", "Europe/Athens", @@ -21,4 +21,4 @@ static constexpr const char *EUROPE_TIMEZONES[] = { constexpr unsigned int EUROPE_TIMEZONES_SIZE = sizeof(EUROPE_TIMEZONES) / sizeof(EUROPE_TIMEZONES[0]); -#endif // LIB_COMMUNICATION_TIMEZONES_H_ +#endif // CLI_COMMUNICATION_TIMEZONES_H_ diff --git a/lib/communication/user_agents.h b/cli/communication/user_agents.h similarity index 98% rename from lib/communication/user_agents.h rename to cli/communication/user_agents.h index 365b4db..80e29dc 100644 --- a/lib/communication/user_agents.h +++ b/cli/communication/user_agents.h @@ -1,5 +1,5 @@ -#ifndef LIB_COMMUNICATION_USER_AGENTS_H_ -#define LIB_COMMUNICATION_USER_AGENTS_H_ +#ifndef CLI_COMMUNICATION_USER_AGENTS_H_ +#define CLI_COMMUNICATION_USER_AGENTS_H_ // // https://github.com/SaswatPadhi/FlashProfileDemo/blob/c1e3f05d09f6443568a606dc0a439d6ebb057ae1/tests/hetero/user_agents.json @@ -109,4 +109,4 @@ static constexpr const char *USER_AGENTS[] = { constexpr unsigned int USER_AGENTS_SIZE = sizeof(USER_AGENTS) / sizeof(USER_AGENTS[0]); -#endif // LIB_COMMUNICATION_USER_AGENTS_H_ +#endif // CLI_COMMUNICATION_USER_AGENTS_H_ diff --git a/lib/utils/uuid4.h b/cli/utils/uuid4.h similarity index 90% rename from lib/utils/uuid4.h rename to cli/utils/uuid4.h index df563b0..f9152d8 100644 --- a/lib/utils/uuid4.h +++ b/cli/utils/uuid4.h @@ -1,5 +1,5 @@ -#ifndef LIB_UTILS_UUID4_H_ -#define LIB_UTILS_UUID4_H_ +#ifndef CLI_UTILS_UUID4_H_ +#define CLI_UTILS_UUID4_H_ #include #include @@ -47,4 +47,4 @@ std::string generate() } } // namespace uuid4 -#endif // LIB_UTILS_UUID4_H_ +#endif // CLI_UTILS_UUID4_H_ diff --git a/include/vibra.h b/include/vibra.h index a517b29..89f8e47 100644 --- a/include/vibra.h +++ b/include/vibra.h @@ -89,34 +89,6 @@ const char *vibra_get_uri_from_fingerprint(Fingerprint *fingerprint); * @note This function is thread-unsafe. */ unsigned int vibra_get_sample_ms_from_fingerprint(Fingerprint *fingerprint); - -/** - * @brief Generate a Shazam request JSON from a fingerprint. - * - * @param fingerprint Pointer to the fingerprint. - * @return const char* The Shazam request JSON as a C-string. - * - * @note This function is thread-unsafe and the returned pointer should not be freed. - */ -const char *vibra_get_shazam_request_json(const Fingerprint *fingerprint); - -/** - * @brief Get the Shazam host URL. - * - * @return const char* The Shazam host URL as a C-string. - * - * @note This function is thread-unsafe and the returned pointer should not be freed. - */ -const char *vibra_get_shazam_host(); - -/** - * @brief Get a random Shazam user agent string. - * - * @return const char* A random Shazam user agent as a C-string. - * - * @note This function is thread-unsafe and the returned pointer should not be freed. - */ -const char *vibra_get_shazam_random_user_agent(); } // extern "C" #endif // INCLUDE_VIBRA_H_ diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index da0a7b8..3a97e60 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -6,7 +6,6 @@ set(LIBVIBRA_SOURCES algorithm/signature_generator.cpp audio/wav.cpp audio/downsampler.cpp - communication/shazam.cpp ) # Add shared and static libraries for libvibra diff --git a/lib/algorithm/frequency.cpp b/lib/algorithm/frequency.cpp index ef50a82..790f384 100644 --- a/lib/algorithm/frequency.cpp +++ b/lib/algorithm/frequency.cpp @@ -1,28 +1,28 @@ #include "algorithm/frequency.h" #include -FrequancyPeak::FrequancyPeak(std::uint32_t fft_pass_number, std::uint32_t peak_magnitude, +FrequencyPeak::FrequencyPeak(std::uint32_t fft_pass_number, std::uint32_t peak_magnitude, std::uint32_t corrected_peak_frequency_bin, std::uint32_t sample_rate) : fft_pass_number_(fft_pass_number), peak_magnitude_(peak_magnitude), corrected_peak_frequency_bin_(corrected_peak_frequency_bin), sample_rate_(sample_rate) { } -FrequancyPeak::~FrequancyPeak() +FrequencyPeak::~FrequencyPeak() { } -double FrequancyPeak::ComputeFrequency() const +double FrequencyPeak::ComputeFrequency() const { return corrected_peak_frequency_bin_ * (static_cast(sample_rate_) / 2. / 1024. / 64.); } -double FrequancyPeak::ComputeAmplitudePCM() const +double FrequencyPeak::ComputeAmplitudePCM() const { return std::sqrt(std::exp((peak_magnitude_ - 6144) / 1477.3) * (1 << 17) / 2.) / 1024.; } -double FrequancyPeak::ComputeElapsedSeconds() const +double FrequencyPeak::ComputeElapsedSeconds() const { return static_cast(fft_pass_number_) * 128. / static_cast(sample_rate_); } diff --git a/lib/algorithm/frequency.h b/lib/algorithm/frequency.h index 7183102..fd87b8a 100644 --- a/lib/algorithm/frequency.h +++ b/lib/algorithm/frequency.h @@ -3,7 +3,7 @@ #include -enum class FrequancyBand +enum class FrequencyBand { _0_150 = -1, _250_520, @@ -12,12 +12,12 @@ enum class FrequancyBand _3500_5500, }; -class FrequancyPeak +class FrequencyPeak { - public: - FrequancyPeak(std::uint32_t fft_pass_number, std::uint32_t peak_magnitude, +public: + FrequencyPeak(std::uint32_t fft_pass_number, std::uint32_t peak_magnitude, std::uint32_t corrected_peak_frequency_bin, std::uint32_t sample_rate); - ~FrequancyPeak(); + ~FrequencyPeak(); inline std::uint32_t fft_pass_number() const { @@ -35,7 +35,7 @@ class FrequancyPeak inline double ComputeAmplitudePCM() const; inline double ComputeElapsedSeconds() const; - private: +private: std::uint32_t fft_pass_number_; std::uint32_t peak_magnitude_; std::uint32_t corrected_peak_frequency_bin_; diff --git a/lib/algorithm/signature.cpp b/lib/algorithm/signature.cpp index 30e6caa..6e010a1 100644 --- a/lib/algorithm/signature.cpp +++ b/lib/algorithm/signature.cpp @@ -14,13 +14,13 @@ void Signature::Reset(std::uint32_t sampleRate, std::uint32_t num_samples) { sample_rate_ = sampleRate; num_samples_ = num_samples; - frequancy_band_to_peaks_.clear(); + frequency_band_to_peaks_.clear(); } std::uint32_t Signature::SumOfPeaksLength() const { std::uint32_t sum = 0; - for (const auto &pair : frequancy_band_to_peaks_) + for (const auto &pair : frequency_band_to_peaks_) { sum += pair.second.size(); } @@ -37,7 +37,7 @@ std::string Signature::EncodeBase64() const header.number_samples_plus_divided_sample_rate = static_cast(num_samples_ + sample_rate_ * 0.24); std::stringstream contents; - for (const auto &pair : frequancy_band_to_peaks_) + for (const auto &pair : frequency_band_to_peaks_) { const auto &band = pair.first; const auto &peaks = pair.second; diff --git a/lib/algorithm/signature.h b/lib/algorithm/signature.h index fa71b71..f90b780 100644 --- a/lib/algorithm/signature.h +++ b/lib/algorithm/signature.h @@ -32,7 +32,7 @@ __attribute__((packed)); class Signature { - public: +public: Signature(std::uint32_t sample_rate, std::uint32_t num_samples); ~Signature(); void Reset(std::uint32_t sampleRate, std::uint32_t num_samples); @@ -49,17 +49,17 @@ class Signature { return num_samples_; } - inline std::map> &frequancy_band_to_peaks() + inline std::map> &frequency_band_to_peaks() { - return frequancy_band_to_peaks_; + return frequency_band_to_peaks_; } std::uint32_t SumOfPeaksLength() const; std::string EncodeBase64() const; - private: +private: template std::stringstream &write_little_endian(std::stringstream &stream, const T &&value, - size_t size = sizeof(T)) const + size_t size = sizeof(T)) const { for (size_t i = 0; i < size; ++i) { @@ -68,10 +68,10 @@ class Signature return stream; } - private: +private: std::uint32_t sample_rate_; std::uint32_t num_samples_; - std::map> frequancy_band_to_peaks_; + std::map> frequency_band_to_peaks_; }; #endif // LIB_ALGORITHM_SIGNATURE_H_ diff --git a/lib/algorithm/signature_generator.cpp b/lib/algorithm/signature_generator.cpp index b93361e..258ff8c 100644 --- a/lib/algorithm/signature_generator.cpp +++ b/lib/algorithm/signature_generator.cpp @@ -8,8 +8,8 @@ SignatureGenerator::SignatureGenerator() : input_pending_processing_(), sample_processed_(0), max_time_seconds_(3.1), - next_signature_(16000, 0), samples_ring_buffer_(2048, 0), - fft_outputs_(256, fft::RealArray(1025, 0.0)), + fft_object_(FFT_BUFFER_CHUNK_SIZE), next_signature_(16000, 0), + samples_ring_buffer_(FFT_BUFFER_CHUNK_SIZE, 0), fft_outputs_(256, fft::RealArray(1025, 0.0)), spread_ffts_output_(256, fft::RealArray(1025, 0.0)) { } @@ -63,24 +63,25 @@ void SignatureGenerator::doFFT(const LowQualityTrack &input) samples_ring_buffer_.begin() + samples_ring_buffer_.position()); samples_ring_buffer_.position() += input.size(); - samples_ring_buffer_.position() %= 2048; + samples_ring_buffer_.position() %= FFT_BUFFER_CHUNK_SIZE; samples_ring_buffer_.num_written() += input.size(); - fft::RealArray excerpt_from_ring_buffer(2048, 0.0); + fft::RealArray excerpt_from_ring_buffer(FFT_BUFFER_CHUNK_SIZE, 0.0); std::copy(samples_ring_buffer_.begin() + samples_ring_buffer_.position(), samples_ring_buffer_.end(), excerpt_from_ring_buffer.begin()); std::copy(samples_ring_buffer_.begin(), samples_ring_buffer_.begin() + samples_ring_buffer_.position(), - excerpt_from_ring_buffer.begin() + 2048 - samples_ring_buffer_.position()); + excerpt_from_ring_buffer.begin() + FFT_BUFFER_CHUNK_SIZE - + samples_ring_buffer_.position()); - for (int i = 0; i < 2048; ++i) + for (std::size_t i = 0; i < FFT_BUFFER_CHUNK_SIZE; ++i) { excerpt_from_ring_buffer[i] *= HANNIG_MATRIX[i]; } - fft::RealArray real = fft::FFT::RFFT(excerpt_from_ring_buffer); + fft::RealArray real = fft_object_.RFFT(excerpt_from_ring_buffer); fft_outputs_.Append(real); } @@ -170,28 +171,28 @@ void SignatureGenerator::doPeakRecognition() auto frequency_hz = corrected_peak_frequency_bin * (16000.0l / 2. / 1024. / 64.); - auto band = FrequancyBand(); + auto band = FrequencyBand(); if (frequency_hz < 250) continue; else if (frequency_hz < 520) - band = FrequancyBand::_250_520; + band = FrequencyBand::_250_520; else if (frequency_hz < 1450) - band = FrequancyBand::_520_1450; + band = FrequencyBand::_520_1450; else if (frequency_hz < 3500) - band = FrequancyBand::_1450_3500; + band = FrequencyBand::_1450_3500; else if (frequency_hz <= 5500) - band = FrequancyBand::_3500_5500; + band = FrequencyBand::_3500_5500; else continue; - auto &band_to_sound_peaks = next_signature_.frequancy_band_to_peaks(); + auto &band_to_sound_peaks = next_signature_.frequency_band_to_peaks(); if (band_to_sound_peaks.find(band) == band_to_sound_peaks.end()) { - band_to_sound_peaks[band] = std::list(); + band_to_sound_peaks[band] = std::list(); } band_to_sound_peaks[band].push_back( - FrequancyPeak(fft_number, static_cast(peak_magnitude), + FrequencyPeak(fft_number, static_cast(peak_magnitude), static_cast(corrected_peak_frequency_bin), LOW_QUALITY_SAMPLE_RATE)); } @@ -203,7 +204,7 @@ void SignatureGenerator::doPeakRecognition() void SignatureGenerator::resetSignatureGenerater() { next_signature_ = Signature(16000, 0); - samples_ring_buffer_ = RingBuffer(2048, 0); + samples_ring_buffer_ = RingBuffer(FFT_BUFFER_CHUNK_SIZE, 0); fft_outputs_ = RingBuffer(256, fft::RealArray(1025, 0.0)); spread_ffts_output_ = RingBuffer(256, fft::RealArray(1025, 0.0)); } diff --git a/lib/algorithm/signature_generator.h b/lib/algorithm/signature_generator.h index 94cbaff..bd08581 100644 --- a/lib/algorithm/signature_generator.h +++ b/lib/algorithm/signature_generator.h @@ -6,11 +6,12 @@ #include "utils/fft.h" #include "utils/ring_buffer.h" -constexpr auto MAX_PEAKS = 255u; +constexpr std::size_t MAX_PEAKS = 255u; +constexpr std::size_t FFT_BUFFER_CHUNK_SIZE = 2048u; class SignatureGenerator { - public: +public: SignatureGenerator(); void FeedInput(const LowQualityTrack &input); Signature GetNextSignature(); @@ -25,7 +26,7 @@ class SignatureGenerator max_time_seconds_ = max_time_seconds; } - private: +private: void processInput(const LowQualityTrack &input); void doFFT(const LowQualityTrack &input); void doPeakSpreadingAndRecoginzation(); @@ -33,11 +34,12 @@ class SignatureGenerator void doPeakRecognition(); void resetSignatureGenerater(); - private: +private: LowQualityTrack input_pending_processing_; std::uint32_t sample_processed_; double max_time_seconds_; + fft::FFT fft_object_; Signature next_signature_; RingBuffer samples_ring_buffer_; RingBuffer fft_outputs_; diff --git a/lib/audio/downsampler.h b/lib/audio/downsampler.h index bae4a27..d38b138 100644 --- a/lib/audio/downsampler.h +++ b/lib/audio/downsampler.h @@ -20,11 +20,11 @@ using DownsampleFunc = void (*)(LowQualityTrack *, const void *, double, std::ui class Downsampler { - public: +public: static LowQualityTrack GetLowQualityPCM(const Wav &wav, std::int32_t start_sec = 0, std::int32_t end_sec = -1); - private: +private: static DownsampleFunc getDownsampleFunc(bool is_signed, std::uint32_t width, std::uint32_t channels); diff --git a/lib/audio/wav.h b/lib/audio/wav.h index d9f60c3..079526b 100644 --- a/lib/audio/wav.h +++ b/lib/audio/wav.h @@ -30,7 +30,7 @@ enum class AudioFormat class Wav { - public: +public: Wav(Wav &&) = default; Wav(const Wav &) = delete; static Wav FromFile(const std::string &wav_file_path); @@ -72,14 +72,14 @@ class Wav return data_; } - private: +private: Wav() = default; static Wav fromPCM(const char *raw_pcm, std::uint32_t raw_pcm_size, AudioFormat audio_format, std::uint32_t sample_rate, std::uint32_t sample_width, std::uint32_t channel_count); void readWavFileBuffer(std::istream &stream); - private: +private: WavHeader header_; FmtSubchunk fmt_; std::string wav_file_path_; diff --git a/lib/communication/shazam.cpp b/lib/communication/shazam.cpp deleted file mode 100644 index 5fb9ff3..0000000 --- a/lib/communication/shazam.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "communication/shazam.h" -#include -#include -#include -#include "algorithm/signature.h" -#include "communication/timezones.h" -#include "communication/user_agents.h" -#include "utils/uuid4.h" - -// static variables initialization -constexpr char Shazam::HOST[]; - -std::string Shazam::GetShazamHost() -{ - std::string host = HOST + uuid4::generate() + "/" + uuid4::generate(); - host += "?sync=true&" - "webv3=true&" - "sampling=true&" - "connected=&" - "shazamapiversion=v3&" - "sharehub=true&" - "video=v3"; - return host; -} - -std::string Shazam::GetRequestContent(const std::string &uri, unsigned int sample_ms) -{ - std::mt19937 gen(std::random_device{}()); - std::uniform_int_distribution<> dis_float(0.0, 1.0); - - auto timezone = getTimezone(); - double fuzz = dis_float(gen) * 15.3 - 7.65; - - std::stringstream json_buf; - json_buf << "{"; - json_buf << "\"geolocation\":{"; - json_buf << "\"altitude\":" << dis_float(gen) * 400 + 100 + fuzz << ","; - json_buf << "\"latitude\":" << dis_float(gen) * 180 - 90 + fuzz << ","; - json_buf << "\"longitude\":" << dis_float(gen) * 360 - 180 + fuzz; - json_buf << "},"; - json_buf << "\"signature\":{"; - json_buf << "\"samplems\":" << sample_ms << ","; - json_buf << "\"timestamp\":" << time(nullptr) * 1000ULL << ","; - json_buf << "\"uri\":\"" << uri << "\""; - json_buf << "},"; - json_buf << "\"timestamp\":" << time(nullptr) * 1000ULL << ","; - json_buf << "\"timezone\":" - << "\"" << timezone << "\""; - json_buf << "}"; - std::string content = json_buf.str(); - return content; -} - -std::string Shazam::GetUserAgent() -{ - std::mt19937 gen(std::random_device{}()); - std::uniform_int_distribution<> dis_useragent(0, USER_AGENTS_SIZE - 1); - return USER_AGENTS[dis_useragent(gen)]; -} - -std::string Shazam::getTimezone() -{ - std::mt19937 gen(std::random_device{}()); - std::uniform_int_distribution<> dis_timezone(0, EUROPE_TIMEZONES_SIZE - 1); - return EUROPE_TIMEZONES[dis_timezone(gen)]; -} diff --git a/lib/communication/shazam.h b/lib/communication/shazam.h deleted file mode 100644 index 16c43b6..0000000 --- a/lib/communication/shazam.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef LIB_COMMUNICATION_SHAZAM_H_ -#define LIB_COMMUNICATION_SHAZAM_H_ - -#include - -class Shazam -{ - static constexpr char HOST[] = "https://amp.shazam.com/discovery/v5/fr/FR/android/-/tag/"; - - public: - static std::string GetShazamHost(); - static std::string GetUserAgent(); - static std::string GetRequestContent(const std::string &uri, unsigned int sample_ms); - - private: - static std::string getTimezone(); -}; - -#endif // LIB_COMMUNICATION_SHAZAM_H_ diff --git a/lib/utils/ffmpeg.h b/lib/utils/ffmpeg.h index 36c212b..da42533 100644 --- a/lib/utils/ffmpeg.h +++ b/lib/utils/ffmpeg.h @@ -18,11 +18,11 @@ constexpr int EXPECTED_DURATION = 5 * 60; // 5 minutes class FFmpegWrapper { - public: +public: FFmpegWrapper() = delete; static int ConvertToWav(const std::string &input_file, LowQualityTrack *pcm); - private: +private: static std::string getFFmpegPath(); static bool isWindows(); }; diff --git a/lib/utils/fft.h b/lib/utils/fft.h index a19f500..914fef4 100644 --- a/lib/utils/fft.h +++ b/lib/utils/fft.h @@ -4,6 +4,7 @@ #include #include #include // NOLINT [include_order] +#include #include namespace fft @@ -13,29 +14,36 @@ using RealArray = std::vector; class FFT { - public: - template static RealArray RFFT(const Iterable &input) +public: + explicit FFT(std::uint32_t input_size) + : input_size_(input_size), + input_data_buffer_(fftw_alloc_real(input_size), fftw_free), + output_data_buffer_(fftw_alloc_complex(input_size / 2 + 1), fftw_free) { - // If input size is 0, return false. - if (input.empty()) + fftw_plan_ = fftw_plan_dft_r2c_1d(input_size, input_data_buffer_.get(), + output_data_buffer_.get(), FFTW_ESTIMATE); + } + FFT(const FFT &) = delete; + FFT &operator=(const FFT &) = delete; + FFT(FFT &&) = delete; + FFT &operator=(FFT &&) = delete; + + template RealArray RFFT(const Iterable &input) + { + if (input.size() != input_size_) { - throw std::invalid_argument("Input size cannot be 0"); + throw std::invalid_argument( + "Input size must be equal to the input size specified in the constructor"); } - std::size_t input_size = input.size(); - RealArray real_output(input_size / 2 + 1); - - double *in = fftw_alloc_real(input_size); - fftw_complex *out = fftw_alloc_complex(input_size / 2 + 1); + RealArray real_output(input_size_ / 2 + 1); // Copy and convert the input data to double - for (std::size_t i = 0; i < input_size; i++) + for (std::size_t i = 0; i < input_size_; i++) { - in[i] = static_cast(input[i]); + input_data_buffer_.get()[i] = static_cast(input[i]); } - - fftw_plan plan = fftw_plan_dft_r2c_1d(input_size, in, out, FFTW_ESTIMATE); - fftw_execute(plan); + fftw_execute(fftw_plan_); double real_val = 0.0; double imag_val = 0.0; @@ -43,24 +51,29 @@ class FFT const double scale_factor = 1.0 / (1 << 17); // do max((real^2 + imag^2) / (1 << 17), 0.0000000001) - for (std::size_t i = 0; i < input_size / 2 + 1; ++i) + for (std::size_t i = 0; i < input_size_ / 2 + 1; ++i) { - real_val = out[i][0]; - imag_val = out[i][1]; + real_val = output_data_buffer_.get()[i][0]; + imag_val = output_data_buffer_.get()[i][1]; real_val = (real_val * real_val + imag_val * imag_val) * scale_factor; real_output[i] = (real_val < min_val) ? min_val : real_val; } - - // Clean up - fftw_destroy_plan(plan); - fftw_free(in); - fftw_free(out); - return real_output; } -}; + virtual ~FFT() + { + fftw_destroy_plan(fftw_plan_); + fftw_cleanup(); + } + +private: + std::uint32_t input_size_; + fftw_plan fftw_plan_; + std::unique_ptr input_data_buffer_; + std::unique_ptr output_data_buffer_; +}; } // namespace fft #endif // LIB_UTILS_FFT_H_ diff --git a/lib/utils/ring_buffer.h b/lib/utils/ring_buffer.h index 6599083..82c28fc 100644 --- a/lib/utils/ring_buffer.h +++ b/lib/utils/ring_buffer.h @@ -5,7 +5,7 @@ template class RingBuffer : private std::vector { - public: +public: explicit RingBuffer(std::size_t size, T &&default_value = T()); virtual ~RingBuffer(); @@ -34,7 +34,7 @@ template class RingBuffer : private std::vector return std::vector::end(); } - private: +private: std::uint32_t num_written_; std::uint32_t position_; }; diff --git a/lib/vibra.cpp b/lib/vibra.cpp index b7ca77c..623db90 100644 --- a/lib/vibra.cpp +++ b/lib/vibra.cpp @@ -2,7 +2,6 @@ #include "algorithm/signature_generator.h" #include "audio/downsampler.h" #include "audio/wav.h" -#include "communication/shazam.h" #include "utils/ffmpeg.h" Fingerprint *_get_fingerprint_from_wav(const Wav &wav); @@ -55,27 +54,6 @@ unsigned int vibra_get_sample_ms_from_fingerprint(Fingerprint *fingerprint) return fingerprint->sample_ms; } -const char *vibra_get_shazam_request_json(const Fingerprint *fingerprint) -{ - static std::string content; - content = Shazam::GetRequestContent(fingerprint->uri, fingerprint->sample_ms); - return content.c_str(); -} - -const char *vibra_get_shazam_host() -{ - static std::string host; - host = Shazam::GetShazamHost(); - return host.c_str(); -} - -const char *vibra_get_shazam_random_user_agent() -{ - static std::string user_agent; - user_agent = Shazam::GetUserAgent(); - return user_agent.c_str(); -} - Fingerprint *_get_fingerprint_from_wav(const Wav &wav) { LowQualityTrack pcm = Downsampler::GetLowQualityPCM(wav);