/
reds
/
OpenDAW
Обзор
Документация
Войти
/
reds
/
OpenDAW
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
tests/unit/test_audio_graph.cpp
348 строк
9 KB
OpenDAW Developer
feat(v1.0.0-phase1): Add AudioGraph foundation
21 фев 2026, 08:49
21 фев 2026, 08:49
2a0b0b6
Код
Авторство
О чём код?
// AudioGraph Unit Tests #include <opendaw/core/AudioGraph.h> #include <opendaw/dsp/MidiSynthesizer.h> #include <gtest/gtest.h> #include <cmath> using namespace opendaw; // ============================================================================ // AudioNode Tests // ============================================================================ TEST(AudioNodeTest, CanCreateTrack) { auto track = std::make_unique<Track>("Test Track", TrackType::Audio); EXPECT_NE(track, nullptr); EXPECT_EQ(track->getName(), "Test Track"); EXPECT_EQ(track->getType(), TrackType::Audio); EXPECT_TRUE(track->isActive()); } TEST(AudioNodeTest, TrackPrepare) { Track track("Test", TrackType::Audio); // Не должно падать track.prepare(48000.0, 512); EXPECT_TRUE(true); } TEST(AudioNodeTest, TrackProcess) { Track track("Test", TrackType::Audio); track.prepare(48000.0, 512); AudioBuffer<float> audio(2, 512); MidiBuffer midi; // Заполнить аудио сигналом for (int ch = 0; ch < 2; ++ch) { auto* data = audio.getChannel(ch); for (int i = 0; i < 512; ++i) { data[i] = 0.5f; } } // Обработать (не должно падать) track.process(audio, midi); EXPECT_TRUE(true); } TEST(AudioNodeTest, TrackMute) { Track track("Test", TrackType::Audio); track.prepare(48000.0, 512); AudioBuffer<float> audio(2, 512); MidiBuffer midi; // Заполнить сигналом for (int ch = 0; ch < 2; ++ch) { auto* data = audio.getChannel(ch); for (int i = 0; i < 512; ++i) { data[i] = 1.0f; } } // Обработать без mute track.process(audio, midi); // Сигнал должен остаться (с учётом gain) bool hasSignal = false; for (int ch = 0; ch < 2; ++ch) { for (int i = 0; i < 512; ++i) { if (std::abs(audio.getSample(ch, i)) > 0.001f) { hasSignal = true; } } } EXPECT_TRUE(hasSignal); // Включить mute track.setMuted(true); audio.clear(); // Заполнить снова for (int ch = 0; ch < 2; ++ch) { auto* data = audio.getChannel(ch); for (int i = 0; i < 512; ++i) { data[i] = 1.0f; } } // Обработать с mute track.process(audio, midi); // Сигнал должен быть очищен for (int ch = 0; ch < 2; ++ch) { for (int i = 0; i < 512; ++i) { EXPECT_FLOAT_EQ(audio.getSample(ch, i), 0.0f); } } } TEST(AudioNodeTest, TrackVolume) { Track track("Test", TrackType::Audio); track.prepare(48000.0, 512); // Установить громкость 0.5 track.setVolume(0.5f); EXPECT_FLOAT_EQ(track.getVolume(), 0.5f); AudioBuffer<float> audio(2, 512); MidiBuffer midi; // Заполнить сигналом 1.0 for (int ch = 0; ch < 2; ++ch) { auto* data = audio.getChannel(ch); for (int i = 0; i < 512; ++i) { data[i] = 1.0f; } } // Обработать track.process(audio, midi); // Сигнал должен быть 0.5 for (int ch = 0; ch < 2; ++ch) { for (int i = 0; i < 512; ++i) { EXPECT_NEAR(audio.getSample(ch, i), 0.5f, 0.01f); } } } // ============================================================================ // AudioGraph Tests // ============================================================================ TEST(AudioGraphTest, CanCreateGraph) { AudioGraph graph; EXPECT_NE(graph.getMaster(), nullptr); EXPECT_EQ(graph.getNumTracks(), 0); } TEST(AudioGraphTest, CanAddTrack) { AudioGraph graph; auto* track1 = graph.addTrack("Audio 1", TrackType::Audio); auto* track2 = graph.addTrack("MIDI 1", TrackType::Midi); EXPECT_NE(track1, nullptr); EXPECT_NE(track2, nullptr); EXPECT_EQ(graph.getNumTracks(), 2); EXPECT_EQ(track1->getType(), TrackType::Audio); EXPECT_EQ(track2->getType(), TrackType::Midi); } TEST(AudioGraphTest, CanRemoveTrack) { AudioGraph graph; graph.addTrack("Audio 1", TrackType::Audio); graph.addTrack("Audio 2", TrackType::Audio); EXPECT_EQ(graph.getNumTracks(), 2); graph.removeTrack(0); EXPECT_EQ(graph.getNumTracks(), 1); EXPECT_EQ(graph.getTrack(0)->getName(), "Audio 2"); } TEST(AudioGraphTest, GetTrackByIndex) { AudioGraph graph; graph.addTrack("Audio 1", TrackType::Audio); graph.addTrack("MIDI 1", TrackType::Midi); auto* track0 = graph.getTrack(0); auto* track1 = graph.getTrack(1); EXPECT_NE(track0, nullptr); EXPECT_NE(track1, nullptr); EXPECT_EQ(track0->getName(), "Audio 1"); EXPECT_EQ(track1->getName(), "MIDI 1"); } TEST(AudioGraphTest, PrepareGraph) { AudioGraph graph; graph.addTrack("Audio 1", TrackType::Audio); graph.addTrack("MIDI 1", TrackType::Midi); // Не должно падать graph.prepare(48000.0, 512); EXPECT_DOUBLE_EQ(graph.getSampleRate(), 48000.0); EXPECT_EQ(graph.getBlockSize(), 512); } TEST(AudioGraphTest, ProcessGraph) { AudioGraph graph; graph.addTrack("Audio 1", TrackType::Audio); graph.prepare(48000.0, 512); AudioBuffer<float> audio(2, 512); MidiBuffer midi; // Заполнить входной сигнал for (int ch = 0; ch < 2; ++ch) { auto* data = audio.getChannel(ch); for (int i = 0; i < 512; ++i) { data[i] = 0.5f; } } // Обработать (не должно падать) graph.process(audio, midi); EXPECT_TRUE(true); } TEST(AudioGraphTest, ProcessGraphWithPlugins) { AudioGraph graph; auto* track = graph.addTrack("Audio 1", TrackType::Audio); // Добавить плагин (синтезатор как тестовый процессор) // В реальном коде здесь будет VST/CLAP плагин graph.prepare(48000.0, 512); AudioBuffer<float> audio(2, 512); MidiBuffer midi; // Заполнить сигналом for (int ch = 0; ch < 2; ++ch) { auto* data = audio.getChannel(ch); for (int i = 0; i < 512; ++i) { data[i] = 0.5f; } } // Обработать graph.process(audio, midi); // Сигнал должен пройти через граф bool hasSignal = false; for (int ch = 0; ch < 2; ++ch) { for (int i = 0; i < 512; ++i) { if (std::abs(audio.getSample(ch, i)) > 0.001f) { hasSignal = true; } } } EXPECT_TRUE(hasSignal); } TEST(AudioGraphTest, MasterTrack) { AudioGraph graph; auto* master = graph.getMaster(); EXPECT_NE(master, nullptr); EXPECT_EQ(master->getName(), "Master"); EXPECT_EQ(master->getType(), TrackType::Master); } TEST(AudioGraphTest, ResetGraph) { AudioGraph graph; graph.addTrack("Audio 1", TrackType::Audio); graph.prepare(48000.0, 512); // Не должно падать graph.reset(); EXPECT_TRUE(true); } TEST(AudioGraphTest, LatencyCalculation) { AudioGraph graph; graph.addTrack("Audio 1", TrackType::Audio); graph.addTrack("Audio 2", TrackType::Audio); int latency = graph.getTotalLatencySamples(); // Пока задержка 0 (нет плагинов) EXPECT_EQ(latency, 0); } // ============================================================================ // Integration Tests // ============================================================================ TEST(IntegrationTest, FullSignalChain) { // Создать граф AudioGraph graph; // Добавить дорожки auto* audioTrack = graph.addTrack("Audio Track", TrackType::Audio); auto* midiTrack = graph.addTrack("MIDI Track", TrackType::Midi); // Настроить дорожки audioTrack->setVolume(0.8f); audioTrack->setPan(0.0f); midiTrack->setVolume(1.0f); // Подготовить graph.prepare(48000.0, 512); // Создать буферы AudioBuffer<float> audio(2, 512); MidiBuffer midi; // Заполнить аудио сигналом for (int ch = 0; ch < 2; ++ch) { auto* data = audio.getChannel(ch); for (int i = 0; i < 512; ++i) { data[i] = 0.5f * std::sin(2.0 * 3.14159 * 440.0 * i / 48000.0); } } // Добавить MIDI ноту midi.addNoteOn(0, 60, 100, 0); midi.addNoteOff(0, 60, 0, 256); // Обработать graph.process(audio, midi); // Проверить что сигнал прошёл bool hasSignal = false; for (int ch = 0; ch < 2; ++ch) { for (int i = 0; i < 512; ++i) { if (std::abs(audio.getSample(ch, i)) > 0.001f) { hasSignal = true; } } } EXPECT_TRUE(hasSignal); } // ============================================================================ // Main // ============================================================================ int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }