using UnityEngine;
///
/// Процедурный генератор звуковых эффектов и атмосферы для вступительной кат-сцены «Синтезия».
/// Все звуки генерируются программно в памяти (AudioClip.Create) и не требуют внешних файлов.
///
public static class IntroCutsceneAudio
{
private static int sampleRate = 44100;
///
/// Низкий давящий гул заброшенного подземного бункера (зацикленный)
///
public static AudioClip CreateBunkerHumClip(float duration = 6f)
{
int samples = (int)(sampleRate * duration);
float[] data = new float[samples];
System.Random rnd = new System.Random(1337);
float lastNoise = 0f;
for (int i = 0; i < samples; i++)
{
float t = (float)i / sampleRate;
// Низкочастотный рокот бункера: 45 Гц + 68 Гц с биениями
float sub = Mathf.Sin(2f * Mathf.PI * 45f * t) * 0.45f;
float drone = Mathf.Sin(2f * Mathf.PI * 68f * t + Mathf.Sin(2f * Mathf.PI * 0.4f * t)) * 0.25f;
// Фильтрованный низкочастотный шум воздуха в вентиляции
float white = ((float)rnd.NextDouble() * 2f - 1f) * 0.15f;
lastNoise = Mathf.Lerp(lastNoise, white, 0.04f);
// Бесшовное зацикливание по краям
float edgeEnv = 1f;
float edgeTime = 0.5f;
if (t < edgeTime) edgeEnv = t / edgeTime;
else if (t > duration - edgeTime) edgeEnv = (duration - t) / edgeTime;
data[i] = (sub + drone + lastNoise) * edgeEnv * 0.7f;
}
AudioClip clip = AudioClip.Create("BunkerHum", samples, 1, sampleRate, false);
clip.SetData(data, 0);
return clip;
}
///
/// Глухой ритмичный стук сердца (двойной удар тук-тук)
///
public static AudioClip CreateHeartbeatClip(float bpm = 65f)
{
float period = 60f / bpm;
int samples = (int)(sampleRate * period);
float[] data = new float[samples];
for (int i = 0; i < samples; i++)
{
float t = (float)i / sampleRate;
float val = 0f;
// Первый удар (Систола, 0.0с - 0.18с)
if (t < 0.20f)
{
float localT = t / 0.20f;
float env = Mathf.Sin(localT * Mathf.PI) * Mathf.Exp(-localT * 3.5f);
float freq = Mathf.Lerp(65f, 38f, localT);
val += Mathf.Sin(2f * Mathf.PI * freq * t) * env * 0.85f;
}
// Второй удар (Диастола, 0.24с - 0.44с)
float t2 = t - 0.24f;
if (t2 >= 0f && t2 < 0.20f)
{
float localT = t2 / 0.20f;
float env = Mathf.Sin(localT * Mathf.PI) * Mathf.Exp(-localT * 4.0f);
float freq = Mathf.Lerp(55f, 34f, localT);
val += Mathf.Sin(2f * Mathf.PI * freq * t2) * env * 0.65f;
}
data[i] = Mathf.Clamp(val, -1f, 1f);
}
AudioClip clip = AudioClip.Create("Heartbeat", samples, 1, sampleRate, false);
clip.SetData(data, 0);
return clip;
}
///
/// Пиканье кардиомонитора (медицинский зуммер)
///
public static AudioClip CreateHeartMonitorBeepClip()
{
float duration = 0.14f;
int samples = (int)(sampleRate * duration);
float[] data = new float[samples];
for (int i = 0; i < samples; i++)
{
float t = (float)i / samples;
float env = Mathf.Sin(t * Mathf.PI);
// Чистый чирп частоты 940 Гц
float tone = Mathf.Sin(2f * Mathf.PI * 940f * ((float)i / sampleRate));
data[i] = tone * env * 0.35f;
}
AudioClip clip = AudioClip.Create("HeartMonitorBeep", samples, 1, sampleRate, false);
clip.SetData(data, 0);
return clip;
}
///
/// Скрежет ногтей по стене и искаженный стон безумия
///
public static AudioClip CreateScratchAndGroanClip()
{
float duration = 3.2f;
int samples = (int)(sampleRate * duration);
float[] data = new float[samples];
System.Random rnd = new System.Random(42);
float lastVal = 0f;
for (int i = 0; i < samples; i++)
{
float t = (float)i / sampleRate;
// Скрежет ногтей по бетону (высокочастотный фрикционный скрежет)
float scratchNoise = ((float)rnd.NextDouble() * 2f - 1f);
float scratchFilt = scratchNoise - lastVal;
lastVal = scratchNoise * 0.45f;
float scratchMod = Mathf.Sin(2f * Mathf.PI * 7.5f * t) * 0.5f + 0.5f;
float scratch = scratchFilt * scratchMod * 0.22f;
// Заглушенный утробный стон
float groanEnv = Mathf.Sin(Mathf.Clamp01(t / 2.8f) * Mathf.PI);
float groanFreq = Mathf.Lerp(140f, 90f, t / 3.2f) + Mathf.Sin(2f * Mathf.PI * 5f * t) * 12f;
float groan = Mathf.Sin(2f * Mathf.PI * groanFreq * t) * groanEnv * 0.35f;
data[i] = (scratch + groan) * 0.75f;
}
AudioClip clip = AudioClip.Create("ScratchGroan", samples, 1, sampleRate, false);
clip.SetData(data, 0);
return clip;
}
///
/// Скрип сжатого в руке металла
///
public static AudioClip CreateMetalGripClip()
{
float duration = 1.4f;
int samples = (int)(sampleRate * duration);
float[] data = new float[samples];
for (int i = 0; i < samples; i++)
{
float t = (float)i / samples;
float env = Mathf.Sin(t * Mathf.PI) * Mathf.Exp(-t * 2.2f);
// Высокий металлический скрип напряжения
float creak = Mathf.Sin(2f * Mathf.PI * (1450f + Mathf.Sin(t * 40f) * 350f) * ((float)i / sampleRate));
float lowClank = Mathf.Sin(2f * Mathf.PI * 220f * ((float)i / sampleRate)) * Mathf.Exp(-t * 8f);
data[i] = (creak * 0.25f + lowClank * 0.45f) * env;
}
AudioClip clip = AudioClip.Create("MetalGrip", samples, 1, sampleRate, false);
clip.SetData(data, 0);
return clip;
}
///
/// Гортанный хищный рык мутанта вдалеке
///
public static AudioClip CreateDistantGrowlClip()
{
float duration = 2.4f;
int samples = (int)(sampleRate * duration);
float[] data = new float[samples];
System.Random rnd = new System.Random(999);
for (int i = 0; i < samples; i++)
{
float t = (float)i / sampleRate;
float env = Mathf.Sin(Mathf.Clamp01(t / 2.4f) * Mathf.PI);
// Вибрирующий гортанный рык
float am = Mathf.Sin(2f * Mathf.PI * 28f * t) * 0.5f + 0.5f;
float freq = Mathf.Lerp(110f, 65f, t / 2.4f);
float tone = Mathf.Sin(2f * Mathf.PI * freq * t) * am;
float noise = ((float)rnd.NextDouble() * 2f - 1f) * 0.35f * am;
data[i] = (tone * 0.6f + noise * 0.4f) * env * 0.65f;
}
AudioClip clip = AudioClip.Create("DistantGrowl", samples, 1, sampleRate, false);
clip.SetData(data, 0);
return clip;
}
///
/// Оглушительный удар металла о стену
///
public static AudioClip CreateMetalClangClip()
{
float duration = 1.8f;
int samples = (int)(sampleRate * duration);
float[] data = new float[samples];
System.Random rnd = new System.Random(555);
for (int i = 0; i < samples; i++)
{
float t = (float)i / samples;
// Ударный импульс
float impEnv = Mathf.Exp(-t * 22f);
float ringEnv = Mathf.Exp(-t * 3.5f);
float noise = ((float)rnd.NextDouble() * 2f - 1f) * impEnv * 0.8f;
// Металлические гармоники гаечного ключа (звон 440, 880, 1320, 2400 Гц)
float tone1 = Mathf.Sin(2f * Mathf.PI * 520f * ((float)i / sampleRate));
float tone2 = Mathf.Sin(2f * Mathf.PI * 1150f * ((float)i / sampleRate));
float tone3 = Mathf.Sin(2f * Mathf.PI * 2350f * ((float)i / sampleRate));
float harmonics = (tone1 * 0.45f + tone2 * 0.35f + tone3 * 0.2f) * ringEnv;
data[i] = (noise + harmonics) * 0.9f;
}
AudioClip clip = AudioClip.Create("MetalClang", samples, 1, sampleRate, false);
clip.SetData(data, 0);
return clip;
}
///
/// Яростный визг мутанта при броске
///
public static AudioClip CreateFuriousScreechClip()
{
float duration = 1.6f;
int samples = (int)(sampleRate * duration);
float[] data = new float[samples];
System.Random rnd = new System.Random(777);
for (int i = 0; i < samples; i++)
{
float t = (float)i / samples;
float env = Mathf.Sin(t * Mathf.PI) * Mathf.Exp(-t * 1.8f);
// Резкий частотный спад от 1600 Гц до 450 Гц с жестким дисторшном
float freq = Mathf.Lerp(1600f, 450f, t);
float tone = Mathf.Sin(2f * Mathf.PI * freq * ((float)i / sampleRate));
float noise = ((float)rnd.NextDouble() * 2f - 1f) * 0.4f;
float raw = (tone + noise) * env * 1.5f;
data[i] = Mathf.Clamp(raw, -0.9f, 0.9f); // Сатурация
}
AudioClip clip = AudioClip.Create("FuriousScreech", samples, 1, sampleRate, false);
clip.SetData(data, 0);
return clip;
}
///
/// Низкий басовый кинематографичный удар (Cinematic Boom) под появление логотипа SYNTHESIA
///
public static AudioClip CreateCinematicBoomClip()
{
float duration = 3.5f;
int samples = (int)(sampleRate * duration);
float[] data = new float[samples];
for (int i = 0; i < samples; i++)
{
float t = (float)i / samples;
// Ударный спад
float subEnv = Mathf.Exp(-t * 1.6f);
// Скольжение частоты саб-баса: 110 Гц -> 36 Гц
float freq = Mathf.Lerp(110f, 36f, Mathf.Pow(t, 0.6f));
float sub = Mathf.Sin(2f * Mathf.PI * freq * ((float)i / sampleRate));
// Теплый обертон
float harmonic = Mathf.Sin(2f * Mathf.PI * (freq * 2.05f) * ((float)i / sampleRate)) * 0.35f;
data[i] = (sub + harmonic) * subEnv * 0.95f;
}
AudioClip clip = AudioClip.Create("CinematicBoom", samples, 1, sampleRate, false);
clip.SetData(data, 0);
return clip;
}
}