Привет, Хабр! Меня зовут Артур Валиев. Раз в неделю я делюсь здесь своими рабочими буднями и проектами. Сегодня расскажу, чем занимался в последние дни.
Я веду свой блог и обычно пишу про кодеки и про удаленный доступ. Эта статья — немного особенная: она не только как разработчика, но и как музыканта.
Сейчас с нейросетями стало проще писать решения. Ну, «проще» — это громко сказано. Скорее, стало проще быстрее доходить до прототипа, проверять идеи, не бояться больших кодовых баз и собирать вокруг своей боли рабочий инструмент. Но сама боль от этого никуда не исчезает.
Теперь рассказываю от имени электронного продюсера:
У меня много лет была одна постоянная проблема: цифровая громкость.
Когда я был моложе и писал музыку во FL Studio, я часто задавался вопросом: почему у меня во Fruity Loops звук такой жирный, а в других DAW — какой-то другой? Почему одни сэмплы сразу звучат «мощно», а другие теряются? Почему пресет в синтезаторе вроде бы крутой, но в миксе всё разваливается?
Со временем я понял неприятную вещь: очень часто дело не в магии DAW, не в секретном плагине и не в «аналоговом тепле». Дело в громкости, gain staging и клиппинге. В перегрузе. В том, что сигнал уже на входе почти упирается в цифровой потолок.
Сэмплы, которые мы скачиваем, часто нормализованы почти в 0 dBFS: -0.1, -0.05, иногда вообще около -0.01. Пресеты в синтезаторах могут быть перегружены ещё до того, как вы повесили первый EQ. Потом сверху добавляется компрессия, сатурация, лимитер, ещё один «улучшайзер», и внезапно микс вроде громкий, но не звучит.
Я устал постоянно вручную следить за уровнями, пиками, RMS, динамикой и частотными зонами. Поэтому начал писать свой плагин — Mix Teacher AI.
Это VST3-плагин для Windows, который ставится на дорожку, анализирует сигнал в реальном времени и даёт понятные подсказки: что может быть не так, почему это мешает и какой безопасный первый шаг можно попробовать.
Скачать можно здесь:
https://github.com/vaalimusic/mix-teacher-ai/releases
Зачем вообще нужен такой плагин
Когда учишься сведению, самое сложное — понять не «какой плагин повесить», а что именно не так.
Например:
-
вокал вроде громкий, но мутный;
-
барабаны громкие, но кик не читается;
-
бас мощный в соло, но пропадает в миксе;
-
дорожка звучит жирно, но на самом деле всё пережато;
-
мастер уже клипует, хотя «я же почти ничего не делал».
Обычно новичок в этот момент начинает перебирать плагины: ещё один EQ, ещё один компрессор, ещё один сатурационный плагин, ещё один пресет. Но очень часто проблема лежит раньше — в уровне, динамике, перегрузе или конфликте частотных зон.
Моя мысль простая:
Если ваша музыка не звучит, возможно, вы забыли про самое главное. Для меня 90% сведения начинается с громкости.
Это не значит, что EQ, компрессия, реверберация и аранжировка не важны. Важны. Но если дорожка уже приходит в цепочку перегруженной, слишком громкой или без запаса по headroom, дальше всё становится сложнее.
Mix Teacher AI задуман не как автоматический мастеринг и не как замена звукорежиссёра. Это обучающий анализатор:
увидел проблему -> понял причину -> попробовал безопасный шаг -> послушал в контексте микса
Плагин не говорит: «делай только так». Он говорит: «похоже, здесь может быть проблема; вот почему; вот с чего можно начать».
Что умеет текущая версия
Сейчас в плагине есть:
-
VST3 insert-effect для Windows;
-
realtime-анализ аудио;
-
Peak / RMS / approximate LUFS short-term;
-
Crest Factor;
-
Headroom;
-
Clipping Count;
-
waveform;
-
spectrum;
-
RMS dynamics graph;
-
анализ частотных зон;
-
snapshot спектра в момент проблемы;
-
подсказки на русском и английском;
-
выбор типа источника;
-
настройка чувствительности анализа;
-
Copy JSON для будущей AI-интеграции;
-
экспериментальная ручка GOODIZER.
Плагин можно поставить на вокал, барабаны, бас, синт, гитару, мастер или любую другую дорожку. Он будет собирать метрики и показывать подсказки в зависимости от типа источника.
Выбор источника
Сверху можно выбрать, что именно анализируется:
AutoVocalDrumsDrums BusKickSnareBassGuitarPianoSynthFXMaster
Если стоит Auto, плагин пытается сам примерно определить тип источника. Но для более точных подсказок лучше выбирать вручную.
Например, если выбрать Vocal, анализ будет внимательнее смотреть на:
-
муть в нижней середине;
-
сибилянты;
-
читаемость;
-
скачки громкости между фразами.
Если выбрать Drums Bus, плагин будет больше смотреть на:
-
кик;
-
атаку;
-
хэты;
-
плотность;
-
транзиенты;
-
риск пережатости барабанов.
Анализ уровней
Первая вещь, за которой я хотел следить, — это уровень сигнала.
Плагин показывает базовые метрики:
Peak максимальный пикRMS средняя громкостьLUFS Short примерная краткосрочная громкостьCrest Factor разница между пиком и RMSHeadroom запас до 0 dBFSClipping Count количество клиппингов
Пример подсказки:
Пики почти у цифрового потолка.Почему:Запаса почти нет. EQ, компрессия или сатурация легко доведут сигнал до клиппинга.Что попробовать:
Убавь clip/input gain примерно на 3-5 dB.
Это особенно полезно в ситуации, когда дорожка «звучит нормально», но уже находится слишком близко к 0 dBFS. Дальше любой EQ boost, компрессор, сатурация или лимитер могут сделать сигнал хуже.
Меня всегда раздражала ситуация, когда ты скачиваешь сэмпл, кидаешь его в проект, а он уже почти в потолке. Или открываешь пресет синта, а он звучит эффектно только потому, что просто громкий и перегруженный. В соло кажется мощно, но в миксе начинаются проблемы.
Динамика
Для динамики плагин смотрит не только на общий RMS, но и на активные участки сигнала. Это важно для вокала, рэпа, DnB, барабанов и любых материалов, где между активными моментами есть паузы.
Внутри используются такие значения:
active_rms_p10active_rms_p50active_rms_p90active_rms_range_dbtransient_scoreonset_count
Например, если у вокала сильно скачет громкость между фразами, плагин может подсказать:
Громкость дорожки скачет.Почему:Одни фразы сильно громче других. Из-за этого дорожка то вылезает, то пропадает.Что попробовать:Сначала выровняй clip gain вручную.Потом используй компрессор с умеренным gain reduction.
Для drum bus может появиться другая подсказка:
Drum bus может быть пережат.Почему:Если crest factor низкий, барабаны теряют удар и становятся плоскими.Что попробовать:Ослабь bus compression или сделай меньше gain reduction.
Мне хотелось, чтобы плагин не просто показывал цифры, а объяснял их человеческим языком. Не «crest factor = 5.1 dB», а «барабаны могут быть пережаты, попробуй ослабить bus compression».
Частотные зоны
Плагин анализирует энергию по диапазонам:
20-40 Hz sub rumble40-80 Hz low foundation80-150 Hz low body150-350 Hz mud350-800 Hz boxiness800 Hz-2 kHz tone/body2-5 kHz presence/attack5-9 kHz sibilance/harshness9-16 kHz air
Для вокала это помогает ловить муть и сибилянты.
Пример подсказки для вокала:
Похоже, есть муть в вокале.Evidence:180-350 Hz выше соседних зон.Почему:Нижняя середина часто делает голос тяжёлым и менее читаемым.Что попробовать:Попробуй EQ cut 2-4 dB около 220-300 Hz, Q 1-2.
Пример для сибилянтов:
Есть резкие “с” и “ш”.Почему:Диапазон 5-9 kHz слишком активен.Что попробовать:Попробуй de-esser в районе 6-8 kHz.Цель — уменьшать только резкие согласные на 2-5 dB.
Важно: это не «рецепт на все случаи». Это стартовая точка. После любой подсказки нужно слушать в контексте микса.
Drum Mode
Для барабанов я сделал отдельную логику. Если выбрать Drums, Drums Bus, Kick или Snare, график зон переключается в drum-oriented режим:
KickBoomSnareAtkHatsAirPunchFlat
Мне показалось, что для барабанов удобнее видеть не просто частоты, а музыкальные зоны:
-
Kick— низ и вес; -
Boom— гул; -
Snare— коробочность; -
Atk— атака; -
Hats— резкость хэтов и тарелок; -
Air— верх; -
Punch— транзиенты; -
Flat— риск пережатости.
Пример подсказки:
Хэты или тарелки могут резать ухо.Почему:Для барабанов активная зона 5-9 kHz часто означает резкие хэты, тарелки или атаку сн
Что попробовать:
Если верх режет ухо, попробуй dynamic EQ или мягкий shelf-cut 1-3 dB около 6-9 kHz.
Чувствительность (Показаний)
Есть ручка чувствительности анализа.
Она нужна для сложного материала: DnB-барабаны, быстрые паузы, резкие transient-звуки, тихие хвосты, вокал с большими паузами.
Если плагин слишком часто говорит «сигнал слишком тихий», можно поднять Sensitivity. Тогда анализ будет больше доверять активным участкам, а не последней тишине после остановки playback.
GOODIZER (Компрессор — EQ — сатурация — одна ручка)
Отдельно я добавил экспериментальную центральную ручку GOODIZER.
Важный момент:
0% звук не меняется> 0% включается обработка
На небольших значениях она добавляет мягкую сатурацию, presence и лёгкий safety soft-clip. Это не «магическая кнопка сделать микс идеальным», а творческий эффект.
На drum bus можно попробовать значения примерно:
10-25%
На больших значениях эффект становится заметнее и агрессивнее. В интерфейсе ручка анимируется: чем больше значение, тем ярче и «злее» визуальная реакция.
JSON Export
Кнопка Copy JSON копирует отчёт анализа в буфер обмена.
Пример структуры:
{ "plugin": "Mix Teacher AI", "version": "0.2", "track": { "manual_type": "drums_bus", "detected_type": "drums_bus", "effective_type": "drums_bus" }, "levels": { "peak_dbfs": -3.2, "rms_dbfs": -18.5, "crest_factor_db": 15.3 }, "issues": [ { "kind": "hat_harshness", "title": "Хэты или тарелки могут резать ухо", "action": "Попробуй dynamic EQ или мягкий shelf-cut 1-3 dB около 6-9 kHz." } ]}
Сейчас подсказки в основном rule-based. В будущем этот JSON можно отправлять в локальный AI/Ollama, чтобы получать более развёрнутое объяснение прямо внутри плагина.
Техническая часть
Плагин написан на:
-
C++;
-
JUCE;
-
CMake;
-
VST3.
Целевой формат на первом этапе — VST3 для Windows.
Главный принцип архитектуры: audio callback должен быть лёгким. В нём нельзя делать тяжёлые вычисления, аллокации, долгие операции, работу с UI или что-то, что может затормозить playback.
Поэтому в audio thread выполняется только минимальная работа:
-
audio processing;
-
peak/RMS;
-
clipping count;
-
запись samples в ring buffer.
Более тяжёлые вещи считаются отдельно:
-
spectrum;
-
band energy;
-
dynamics;
-
confidence;
-
issues;
-
JSON report.
CMake и JUCE
JUCE подтягивается через FetchContent, поэтому проект можно собрать без локальной установки JUCE:
include(FetchContent)set(MIX_TEACHER_JUCE_TAG "8.0.14" CACHE STRING "JUCE git tag used to build Mix Teacher")FetchContent_Declare( JUCE GIT_REPOSITORY https://github.com/juce-framework/JUCE.git GIT_TAG ${MIX_TEACHER_JUCE_TAG})FetchContent_MakeAvailable(JUCE)juce_add_plugin(MixTeacher COMPANY_NAME "Arthur Valiev" PLUGIN_MANUFACTURER_CODE ArVa PLUGIN_CODE Mtch FORMATS VST3 PRODUCT_NAME "Mix Teacher")
Параметры плагина
Все параметры живут в AudioProcessorValueTreeState, чтобы DAW могла сохранять состояние проекта:
params.push_back(std::make_unique<juce::AudioParameterChoice>( juce::ParameterID { "trackType", 1 }, "Source", juce::StringArray { "Auto", "Vocal", "Drums", "Drums Bus", "Kick", "Snare", "Bass", "Guitar", "Piano", "Synth", "FX", "Master" }, 0));params.push_back(std::make_unique<juce::AudioParameterFloat>( juce::ParameterID { "sensitivity", 1 }, "Sensitivity", juce::NormalisableRange<float> { 0.0f, 1.0f, 0.01f }, 0.65f));params.push_back(std::make_unique<juce::AudioParameterFloat>( juce::ParameterID { "goodizer", 1 }, "Goodizer", juce::NormalisableRange<float> { 0.0f, 1.0f, 0.01f }, 0.0f));
Audio callback
В processBlock() плагин делает только лёгкие операции:
void MixTeacherAudioProcessor::processBlock( juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages){ juce::ScopedNoDenormals noDenormals; midiMessages.clear(); applyGoodizer(buffer); float peak = 0.0f; double sumSquares = 0.0; int blockClips = 0; for (int sample = 0; sample < numSamples; ++sample) { float mono = 0.0f; for (int channel = 0; channel < totalNumInputChannels; ++channel) { const auto value = buffer.getReadPointer(channel)[sample]; const auto absValue = std::abs(value); peak = juce::jmax(peak, absValue); sumSquares += value * value; if (absValue >= 0.999f) ++blockClips; mono += value; } mono /= static_cast<float>(channelsToAnalyse); if (sample < samplesToStore) monoScratch[static_cast<size_t>(sample)] = mono; } latestPeakLinear.store(peak, std::memory_order_relaxed); latestRmsLinear.store(static_cast<float>(rms), std::memory_order_relaxed); clippingCount.fetch_add(blockClips, std::memory_order_relaxed);}
Если GOODIZER = 0%, обработка звука не выполняется. Если ручка выше нуля, включается творческий DSP.
GOODIZER DSP
GOODIZER сделан как мягкий soundgoodizer-style эффект: drive, saturation, presence и safety soft-clip.
void MixTeacherAudioProcessor::applyGoodizer(juce::AudioBuffer<float>& buffer){ const auto amount = parameters.getRawParameterValue("goodizer")->load(); if (amount <= 0.0001f) return; const auto shaped = amount * amount; const auto drive = 1.0f + shaped * 7.5f; const auto wet = juce::jlimit(0.0f, 0.92f, amount); const auto presence = shaped * 0.42f; for (int channel = 0; channel < channelCount; ++channel) { auto* data = buffer.getWritePointer(channel); auto low = goodizerLowState[channel]; for (int sample = 0; sample < buffer.getNumSamples(); ++sample) { const auto dry = data[sample]; low += lowCoeff * (dry - low); const auto high = dry - low; const auto driven = dry * drive + high * presence; const auto saturated = std::tanh(driven) / std::tanh(drive); const auto excited = saturated + high * presence; const auto mixed = dry + (excited - dry) * wet; data[sample] = std::tanh(mixed * outputTrim * 1.08f); } goodizerLowState[channel] = low; }}
На малых значениях эффект даёт лёгкое уплотнение. На больших — начинает сильнее красить сигнал.
История аудио и анализ
Аудио из callback складывается в FIFO, затем UI/analysis-поток вытаскивает данные в историю:
void MixTeacherAudioProcessor::drainFifo(){ const auto ready = juce::jmin(sampleFifo.getNumReady(), fifoCapacity); sampleFifo.prepareToRead(ready, start1, size1, start2, size2); for (int i = 0; i < size1; ++i) appendToHistory(sampleFifoBuffer[start1 + i]); sampleFifo.finishedRead(size1 + size2);}
Главная идея: audio thread не делает тяжёлый FFT и не строит подсказки. Он только собирает данные.
Snapshot анализа
Плагин собирает один объект AnalysisSnapshot, который потом используют UI и JSON export:
struct AnalysisSnapshot{ juce::String plugin = "Mix Teacher AI"; juce::String version = "0.2"; juce::String trackType = "auto"; juce::String language = "ru"; float peakDbfs = -120.0f; float rmsDbfs = -120.0f; float lufsShortTerm = -120.0f; float crestFactorDb = 0.0f; float headroomDb = 120.0f; float activeRmsP10 = -120.0f; float activeRmsP50 = -120.0f; float activeRmsP90 = -120.0f; BandLevels bands; BandEnergyDb bandDb; DrumProfile drumProfile; std::vector<TeacherIssue> issues; FirstStep firstStep;};
Pipeline анализа выглядит так:
updateDynamics(snapshot);updateHistoryLevels(snapshot);updateSpectrum(snapshot);updateValidity(snapshot);updateTrackDetection(snapshot);updateDrumProfile(snapshot);snapshot.issues = mixteacher::buildTeacherIssues(snapshot);snapshot.firstStep = mixteacher::buildFirstStep(snapshot);
FFT и частотные зоны
Спектр считается не по последней тишине, а по последнему активному участку истории:
const auto activeOffset = juce::jmax(0, findRecentActiveOffset(1.0e-4f));for (int i = 0; i < fftSize; ++i){ const auto offset = activeOffset + (fftSize - 1 - i); const auto sourceIndex = (historyWriteIndex + analysisHistory.size() - 1 - offset) % analysisHistory.size(); fftBuffer[i] = analysisHistory[sourceIndex];}window.multiplyWithWindowingTable(fftBuffer.data(), fftSize);fft.performFrequencyOnlyForwardTransform(fftBuffer.data());
После FFT энергия раскладывается по зонам:
snapshot.bandDb.lowFoundation = bandEnergyDb(40.0f, 80.0f);snapshot.bandDb.lowBody = bandEnergyDb(80.0f, 150.0f);snapshot.bandDb.mud = bandEnergyDb(150.0f, 350.0f);snapshot.bandDb.boxiness = bandEnergyDb(350.0f, 800.0f);snapshot.bandDb.presence = bandEnergyDb(2000.0f, 5000.0f);snapshot.bandDb.sibilance = bandEnergyDb(5000.0f, 9000.0f);snapshot.bandDb.air = bandEnergyDb(9000.0f, 16000.0f);
Проверка валидности
Чтобы плагин не делал выводы по тишине, есть проверка валидности:
const auto usefulRmsDb = snapshot.activeRmsP50 > -119.0f ? snapshot.activeRmsP50 : snapshot.rmsDbfs;if (snapshot.peakDbfs < tooQuietPeak || usefulRmsDb < tooQuietRms){ snapshot.validity.state = ValidityState::tooQuiet; snapshot.validity.isValidForAnalysis = false; return;}
Это особенно важно для DnB, drums и вокала с паузами. Иначе анализатор может смотреть не на активный материал, а на хвост после остановки playback.
JSON export
Кнопка Copy JSON вызывает:
juce::SystemClipboard::copyTextToClipboard(snapshot.toJson());
JSON строится через juce::DynamicObject:
auto* root = new juce::DynamicObject();root->setProperty("plugin", plugin);root->setProperty("version", version);root->setProperty("language", language);root->setProperty("sensitivity", sensitivity);root->setProperty("goodizer_amount", goodizerAmount);auto* levels = new juce::DynamicObject();levels->setProperty("peak_dbfs", peakDbfs);levels->setProperty("rms_dbfs", rmsDbfs);levels->setProperty("crest_factor_db", crestFactorDb);levels->setProperty("clipping_detected", clippingDetected);root->setProperty("levels", levels);return juce::JSON::toString(juce::var(root), true);
Такой отчёт можно будет отправлять в AI/Ollama, чтобы получать более подробные объяснения.
UI
UI написан на стандартных JUCE-компонентах:
juce::ComboBox trackTypeBox;juce::ComboBox explanationModeBox;juce::ComboBox languageBox;juce::Slider sensitivitySlider;juce::Slider goodizerSlider;juce::ToggleButton freezeButton;juce::TextButton resetButton;juce::TextButton copyJsonButton;
Параметры привязаны через attachments:
trackTypeAttachment = std::make_unique<ComboAttachment>( audioProcessor.getParameters(), "trackType", trackTypeBox);goodizerAttachment = std::make_unique<SliderAttachment>( audioProcessor.getParameters(), "goodizer", goodizerSlider);
Центральная ручка GOODIZER дополнительно рисуется вручную: кольца, цвет и анимация зависят от значения параметра.
const auto amount = getGoodizerAmount();const auto colour = goodizerColour(amount);if (amount > 0.001f){ // animated rings g.drawEllipse(...);}
Итоговая архитектура
Технически Mix Teacher AI состоит из трёх слоёв.
1. Audio/DSP layer
passthroughGOODIZERpeak/RMS/clippingFIFO/history
2. Analysis layer
FFTband energyactive RMS percentilesvaliditytrack detectionissue engineJSON
3. UI layer
meterswaveformspectrumdrum zonesteacher cardcontrolsanimated GOODIZER knob
Главный принцип разработки: audio callback должен оставаться лёгким, а вся «умная» логика должна работать по уже накопленной истории сигнала.
Что дальше
В планах:
-
AI/Ollama-интеграция;
-
Deep Analyze mode;
-
сравнение before/after;
-
Mix Hub для нескольких дорожек;
-
сравнение kick/bass;
-
сравнение vocal/instrumental;
-
улучшенный LUFS;
-
более точный анализ сибилянтов;
-
улучшенный transient detection;
-
более красивый интерфейс.
Особенно интересная часть — Mix Hub. Хочется, чтобы несколько инстансов плагина могли обмениваться данными и анализировать не только одну дорожку, а отношения между дорожками: например, конфликт kick/bass или vocal/instrumental. Но тут, моя поддержка тормозится, я никогда не мог и не умел поддерживать такие проекты, хотя взял обещание добавить несколько важных функций от рандомных чуваков с ВК.
Установка
Скачать можно здесь:
https://github.com/vaalimusic/mix-teacher-ai/releases
Установка:
-
Скачать архив.
-
Содержимое архива положить в:
C:\Program Files\Common Files\VST3
-
Сделать rescan VST3-плагинов в DAW.
-
Открыть Mix Teacher AI как VST3 insert-effect.
Заключение
Я это делал для себя, у меня конкретные проблемы с уровнями, если бы мне объяснили 15 лет назад что сэмпл паки и пресеты это твой первый враг, да в целом цифровому звуку меня учил только тот самый чувак который появился пару лет назад. Потому — что в моих краях туда не капали. Да кому я объясняю?)
Я хотел инструмент, который не просто показывает график, а объясняет:
что происходитпочему это может мешатьс чего безопасно начать
Mix Teacher AI пока не претендует на роль финального решения. Это прототип, обучающий помощник и мой способ собрать вместе опыт музыканта, разработчика и человека, который слишком долго воевал с цифровой громкостью.
Буду рад обратной связи от звукорежиссёров, музыкантов и продюсеров: что работает полезно, где подсказки ошибаются, какие режимы стоит добавить и какие реальные сценарии сведения хочется закрыть.
PS: Почему в названии AI — Скопируйте JSON отдайте его на корм нейросетям Вам помогут свести дорожку (я специально сделал что-бы json собрал всю нужную инфу про дорожку). А о политике общения из DAW с нейросетями читайте документацию. Мой StudioOne не позволяет. По крайней мере из той информации что знаю. Да и обсуждать не хочу, это сообщение не тема для разговора, просто пользуйтесь, если нужно что-то улучшить просто напишите мне куда-нибудь, давайте помогать друг другу.
Спасибо! =)
ссылка на оригинал статьи https://habr.com/ru/articles/1055440/