add signal generator sin and more...
This commit is contained in:
9
bin/main.signal_generator.cpp
Normal file
9
bin/main.signal_generator.cpp
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#include <hack/logger/logger.hpp>
|
||||||
|
#include "harmonica.hpp" // IWYU pragma: keep
|
||||||
|
|
||||||
|
auto main() -> int
|
||||||
|
{
|
||||||
|
hr::signal_generator generator;
|
||||||
|
auto s = generator.generate(hr::SIGNAL_TYPE::SIN);
|
||||||
|
hack::log()(s);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
executable(
|
executable(
|
||||||
meson.project_name(),
|
meson.project_name(),
|
||||||
'main.cpp',
|
'main.signal_generator.cpp',
|
||||||
dependencies : deps,
|
dependencies : deps,
|
||||||
cpp_args: args,
|
cpp_args: args,
|
||||||
include_directories : inc
|
include_directories : inc
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <hack/logger/logger.hpp>
|
#include <hack/logger/logger.hpp>
|
||||||
|
|
||||||
#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/using.hpp"
|
||||||
#include "utils/workers/setup.hpp"
|
#include "utils/workers/setup.hpp"
|
||||||
#include "utils/workers/result.hpp"
|
#include "utils/workers/result.hpp"
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ headers = [
|
|||||||
'utils/workers/result.hpp',
|
'utils/workers/result.hpp',
|
||||||
'utils/workers/setup.hpp',
|
'utils/workers/setup.hpp',
|
||||||
'utils/windows/hann/hann.hpp',
|
'utils/windows/hann/hann.hpp',
|
||||||
|
'utils/signal_generator/signal_generator.hpp',
|
||||||
|
|
||||||
# plugins
|
# plugins
|
||||||
'plugins/magnitude/magnitude.hpp',
|
'plugins/magnitude/magnitude.hpp',
|
||||||
@@ -25,6 +26,7 @@ sources = [
|
|||||||
'utils/fvec/fvec.cpp',
|
'utils/fvec/fvec.cpp',
|
||||||
|
|
||||||
'utils/windows/hann/hann.cpp',
|
'utils/windows/hann/hann.cpp',
|
||||||
|
'utils/signal_generator/signal_generator.cpp',
|
||||||
|
|
||||||
# plugins
|
# plugins
|
||||||
'plugins/magnitude/magnitude.cpp',
|
'plugins/magnitude/magnitude.cpp',
|
||||||
|
|||||||
219
src/utils/signal_generator/signal_generator.cpp
Normal file
219
src/utils/signal_generator/signal_generator.cpp
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
#include "signal_generator.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace hr
|
||||||
|
{
|
||||||
|
std::vector<float> signal_generator::generate(SIGNAL_TYPE type, NOISE noise)
|
||||||
|
{
|
||||||
|
std::vector<float> 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<float> signal_generator::sin(NOISE noise)
|
||||||
|
{
|
||||||
|
std::vector<float> 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<float> signal_generator::square(NOISE noise)
|
||||||
|
{
|
||||||
|
std::vector<float> 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<float> signal_generator::triangle(NOISE noise)
|
||||||
|
{
|
||||||
|
std::vector<float> 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<float> signal_generator::saw(NOISE noise)
|
||||||
|
{
|
||||||
|
std::vector<float> 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<float> signal_generator::noise_only()
|
||||||
|
{
|
||||||
|
std::vector<float> 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<float> signal_generator::speech_like(NOISE noise)
|
||||||
|
{
|
||||||
|
std::vector<float> 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<float> signal_generator::three_sines(NOISE noise)
|
||||||
|
{
|
||||||
|
std::vector<float> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
60
src/utils/signal_generator/signal_generator.hpp
Normal file
60
src/utils/signal_generator/signal_generator.hpp
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
// Генерирует простую синусоиду и др. полезности см. 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<float> 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<float> sin(NOISE noise);
|
||||||
|
std::vector<float> square(NOISE noise);
|
||||||
|
std::vector<float> triangle(NOISE noise);
|
||||||
|
std::vector<float> saw(NOISE noise);
|
||||||
|
std::vector<float> noise_only();
|
||||||
|
std::vector<float> speech_like(NOISE noise);
|
||||||
|
std::vector<float> three_sines(NOISE noise);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mt19937 m_gen;
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user