From a0965817d951dc5b82b97c5b83622f7cdb6261bd Mon Sep 17 00:00:00 2001 From: chatlanin Date: Wed, 18 Feb 2026 17:36:12 +0300 Subject: [PATCH] add signal generator sin and more... --- bin/main.signal_generator.cpp | 9 + bin/meson.build | 2 +- src/harmonica.hpp | 3 +- src/meson.build | 2 + .../signal_generator/signal_generator.cpp | 219 ++++++++++++++++++ .../signal_generator/signal_generator.hpp | 60 +++++ 6 files changed, 293 insertions(+), 2 deletions(-) create mode 100644 bin/main.signal_generator.cpp create mode 100644 src/utils/signal_generator/signal_generator.cpp create mode 100644 src/utils/signal_generator/signal_generator.hpp diff --git a/bin/main.signal_generator.cpp b/bin/main.signal_generator.cpp new file mode 100644 index 0000000..b1c5ab9 --- /dev/null +++ b/bin/main.signal_generator.cpp @@ -0,0 +1,9 @@ +#include +#include "harmonica.hpp" // IWYU pragma: keep + +auto main() -> int +{ + hr::signal_generator generator; + auto s = generator.generate(hr::SIGNAL_TYPE::SIN); + hack::log()(s); +} diff --git a/bin/meson.build b/bin/meson.build index f6ed8ee..8fb4b76 100644 --- a/bin/meson.build +++ b/bin/meson.build @@ -1,6 +1,6 @@ executable( meson.project_name(), - 'main.cpp', + 'main.signal_generator.cpp', dependencies : deps, cpp_args: args, include_directories : inc diff --git a/src/harmonica.hpp b/src/harmonica.hpp index 022b287..d78cfba 100644 --- a/src/harmonica.hpp +++ b/src/harmonica.hpp @@ -3,7 +3,8 @@ #include #include -#include "utils/var.hpp" // IWYU pragma: keep +#include "utils/var.hpp" // IWYU pragma: keep +#include "utils/signal_generator/signal_generator.hpp" // IWYU pragma: keep #include "utils/using.hpp" #include "utils/workers/setup.hpp" #include "utils/workers/result.hpp" diff --git a/src/meson.build b/src/meson.build index 80bcacb..0e03417 100644 --- a/src/meson.build +++ b/src/meson.build @@ -12,6 +12,7 @@ headers = [ 'utils/workers/result.hpp', 'utils/workers/setup.hpp', 'utils/windows/hann/hann.hpp', + 'utils/signal_generator/signal_generator.hpp', # plugins 'plugins/magnitude/magnitude.hpp', @@ -25,6 +26,7 @@ sources = [ 'utils/fvec/fvec.cpp', 'utils/windows/hann/hann.cpp', + 'utils/signal_generator/signal_generator.cpp', # plugins 'plugins/magnitude/magnitude.cpp', diff --git a/src/utils/signal_generator/signal_generator.cpp b/src/utils/signal_generator/signal_generator.cpp new file mode 100644 index 0000000..4abd17f --- /dev/null +++ b/src/utils/signal_generator/signal_generator.cpp @@ -0,0 +1,219 @@ +#include "signal_generator.hpp" + +#include + +namespace hr +{ + std::vector signal_generator::generate(SIGNAL_TYPE type, NOISE noise) + { + std::vector res; + switch (type) + { + case SIGNAL_TYPE::SIN: + res = sin(noise); + break; + case SIGNAL_TYPE::SQUARE: + res = square(noise); + break; + case SIGNAL_TYPE::TRIANGLE: + res = triangle(noise); + break; + case SIGNAL_TYPE::SAW: + res = saw(noise); + break; + case SIGNAL_TYPE::SPEECH_LIKE: + res = speech_like(noise); + break; + case SIGNAL_TYPE::NOISE_ONLY: + res = noise_only(); + break; + case SIGNAL_TYPE::THREE_SINES: + res = three_sines(noise); + break; + } + + return res; + } + + std::vector signal_generator::sin(NOISE noise) + { + std::vector signal(m_params.m_samples); + for (int i = 0; i < m_params.m_samples; ++i) + { + float t = i * 0.02f; + signal[i] = m_params.m_amplitude * std::sin(m_params.m_frequency * t * 2 * M_PI); + } + + if (noise == NOISE::YES) + { + // создаем распределение случайных величин + std::uniform_real_distribution<> dist(-m_params.m_noise_level, m_params.m_noise_level); + for (auto& s : signal) s += dist(m_gen); + } + + return signal; + } + + std::vector signal_generator::square(NOISE noise) + { + std::vector signal(m_params.m_samples); + for (int i = 0; i < m_params.m_samples; ++i) + { + float t = i * 0.02f; + float phase = m_params.m_frequency * t * 2 * M_PI; + // Прямоугольный сигнал: +1 когда sin > 0, -1 когда sin < 0 + signal[i] = m_params.m_amplitude * (std::sin(phase) > 0 ? 1.0f : -1.0f); + } + + if (noise == NOISE::YES) + { + std::uniform_real_distribution<> dist(-m_params.m_noise_level, m_params.m_noise_level); + for (auto& s : signal) s += dist(m_gen); + } + + return signal; + } + + std::vector signal_generator::triangle(NOISE noise) + { + std::vector signal(m_params.m_samples); + for (int i = 0; i < m_params.m_samples; ++i) + { + float t = i * 0.02f; + float phase = fmod(m_params.m_frequency * t, 1.0f); // 0..1 + + // Треугольный сигнал: линейный рост от -1 до 1, потом падение + if (phase < 0.25f) signal[i] = m_params.m_amplitude * (4.0f * phase); // 0..1 + else if (phase < 0.75f) signal[i] = m_params.m_amplitude * (2.0f - 4.0f * phase); // 1..-1 + else signal[i] = m_params.m_amplitude * (4.0f * phase - 4.0f); // -1..0 + } + + if (noise == NOISE::YES) + { + std::uniform_real_distribution<> dist(-m_params.m_noise_level, m_params.m_noise_level); + for (auto& s : signal) s += dist(m_gen); + } + + return signal; + } + + std::vector signal_generator::saw(NOISE noise) + { + std::vector signal(m_params.m_samples); + for (int i = 0; i < m_params.m_samples; ++i) + { + float t = i * 0.02f; + float phase = fmod(m_params.m_frequency * t, 1.0f); // 0..1 + + // Пилообразный сигнал: линейный рост от -1 до 1, потом резкий сброс + signal[i] = m_params.m_amplitude * (2.0f * phase - 1.0f); // -1..1 + } + + if (noise == NOISE::YES) + { + std::uniform_real_distribution<> dist(-m_params.m_noise_level, m_params.m_noise_level); + for (auto& s : signal) s += dist(m_gen); + } + + return signal; + } + + std::vector signal_generator::noise_only() + { + std::vector signal(m_params.m_samples); + std::uniform_real_distribution<> dist(-m_params.m_amplitude, m_params.m_amplitude); + + for (int i = 0; i < m_params.m_samples; ++i) + signal[i] = dist(m_gen); + + return signal; + } + + std::vector signal_generator::speech_like(NOISE noise) + { + std::vector signal(m_params.m_samples); + + // Базовая частота (основной тон речи) + float base_freq = m_params.m_frequency; + + // Добавляем гармоники, как в реальной речи + float harmonics[] = { 2.0f, 3.0f, 4.0f, 5.0f }; // гармоники + float harmonics_amp[] = { 0.7f, 0.5f, 0.3f, 0.2f }; // их амплитуды + + // Форманты (резонансные частоты речи) + float formants[] = {500.0f, 1500.0f, 2500.0f, 3500.0f}; + float formants_amp[] = {0.4f, 0.6f, 0.3f, 0.2f}; + + // Модуляция амплитуды (как при произношении слогов) + float envelope_freq = 2.0f; // частота слогов + + for (int i = 0; i < m_params.m_samples; ++i) + { + float t = i * 0.02f; + float value = 0.0f; + + // Основной тон + гармоники + for (int h = 0; h < 4; h++) + value += harmonics_amp[h] * std::sin(2 * M_PI * base_freq * harmonics[h] * t); + + // Форманты (резонансы) + for (int f = 0; f < 4; f++) + value += formants_amp[f] * std::sin(2 * M_PI * formants[f] * t * 0.001f); // kHz to Hz + + // Огибающая (модуляция амплитуды) + float envelope = 0.5f + 0.5f * std::sin(2 * M_PI * envelope_freq * t); + + // Добавляем немного шума (дыхание) + std::uniform_real_distribution<> breath_noise(-0.1f, 0.1f); + float breath = breath_noise(m_gen); + + // И немного низкочастотной модуляции (вибрато) + float vibrato = 0.1f * std::sin(2 * M_PI * 5.0f * t); // 5 Hz vibrato + + signal[i] = m_params.m_amplitude * envelope * (value + breath + vibrato); + } + + // Нормализуем + float max_val = *std::max_element(signal.begin(), signal.end(), + [](float a, float b) { return std::abs(a) < std::abs(b); }); + if (max_val > 0) + for (auto& s : signal) s /= max_val; + + // Добавляем дополнительный шум если нужно + if (noise == NOISE::YES) + { + std::uniform_real_distribution<> dist(-m_params.m_noise_level, m_params.m_noise_level); + for (auto& s : signal) s += dist(m_gen); + } + + return signal; + } + + std::vector signal_generator::three_sines(NOISE noise) + { + std::vector signal(m_params.m_samples); + + // три разные частоты + float f1 = m_params.m_frequency; + float f2 = m_params.m_frequency * 2.f; + + // амплитуды + float a1 = m_params.m_amplitude; + float a2 = m_params.m_amplitude * 0.5f; + + for (int i = 0; i < m_params.m_samples; ++i) + { + float t = i * 0.02f; + signal[i] = a1 * std::sin(2 * M_PI * f1 * t) + + a2 * std::sin(2 * M_PI * f2 * t + 3.f/4.f* M_PI); + } + + if (noise == NOISE::YES) + { + std::uniform_real_distribution<> dist(-m_params.m_noise_level, m_params.m_noise_level); + for (auto& s : signal) s += dist(m_gen); + } + + return signal; + } +} diff --git a/src/utils/signal_generator/signal_generator.hpp b/src/utils/signal_generator/signal_generator.hpp new file mode 100644 index 0000000..51eac7a --- /dev/null +++ b/src/utils/signal_generator/signal_generator.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include + +// Генерирует простую синусоиду и др. полезности см. SIGNAL_TYPE +namespace hr +{ + enum class SIGNAL_TYPE + { + SIN, // Синусоида + SQUARE, // Прямоугольный + TRIANGLE, // Треугольный + SAW, // Пилообразный + NOISE_ONLY, // Только шум + SPEECH_LIKE, // Типа речь + THREE_SINES // Три частоты + }; + + enum class NOISE + { + NO, + YES + }; + + // Формула синусоиды + // f(t) = A_m sin(2 PI t 1/T + a) + // где + // a - начальная фаза + // t - время замера + // 1/T - частота + // T - время полного цикла одного периуда колебаний + class signal_generator + { + public: + signal_generator() : m_gen(12345) {} + + std::vector generate(SIGNAL_TYPE type, NOISE noise = NOISE::NO); + + public: + struct signal_params + { + float m_amplitude = 1.0f; // A + float m_frequency = 1.0f; // 1/T + float m_noise_level = 0.7f; + int m_samples = 1200; + } m_params; + + private: + std::vector sin(NOISE noise); + std::vector square(NOISE noise); + std::vector triangle(NOISE noise); + std::vector saw(NOISE noise); + std::vector noise_only(); + std::vector speech_like(NOISE noise); + std::vector three_sines(NOISE noise); + + private: + std::mt19937 m_gen; + }; +}