using UnityEngine;
///
/// Процедурный синтезатор звуковых эффектов для интерфейса терминала, кнопок и записок.
/// Создает чистые звуки прямо в оперативной памяти (AudioClip.Create) без внешних файлов.
///
public static class GameAudioSynthesizer
{
private static AudioSource globalAudioSource;
private static AudioSource GetAudioSource()
{
if (globalAudioSource == null)
{
var go = new GameObject("ProceduralUIAudioSource");
if (Application.isPlaying)
{
Object.DontDestroyOnLoad(go);
}
globalAudioSource = go.AddComponent();
globalAudioSource.playOnAwake = false;
globalAudioSource.spatialBlend = 0f; // 2D звук интерфейса
globalAudioSource.ignoreListenerPause = true; // Работает даже при Time.timeScale = 0
}
return globalAudioSource;
}
///
/// Короткий высокий бип нажатия кнопки терминала (1200 Hz)
///
public static void PlayKeypadBeep()
{
AudioClip clip = CreateToneClip(1200f, 0.05f, 0.25f);
PlayOneShot(clip, 0.35f);
}
///
/// Звук сброса/стирания цифры (700 Hz)
///
public static void PlayKeypadClear()
{
AudioClip clip = CreateToneClip(700f, 0.08f, 0.3f);
PlayOneShot(clip, 0.3f);
}
///
/// Звук ошибки ввода (низкий двойной зуммер 160Hz)
///
public static void PlayErrorCode()
{
int sampleRate = 44100;
int samples = (int)(sampleRate * 0.25f);
float[] data = new float[samples];
for (int i = 0; i < samples; i++)
{
float t = (float)i / sampleRate;
float freq = 160f;
float pulse = Mathf.Sin(2f * Mathf.PI * 18f * t) > 0f ? 1f : 0f;
data[i] = Mathf.Sin(2f * Mathf.PI * freq * t) * pulse * (1f - (float)i / samples) * 0.4f;
}
AudioClip clip = AudioClip.Create("ErrorCodeTone", samples, 1, sampleRate, false);
clip.SetData(data, 0);
PlayOneShot(clip, 0.45f);
}
///
/// Торжественный трезвук успеха (C-E-G мажорный перелив)
///
public static void PlaySuccessChime()
{
int sampleRate = 44100;
int samples = (int)(sampleRate * 0.6f);
float[] data = new float[samples];
float[] freqs = new float[] { 523.25f, 659.25f, 783.99f, 1046.50f };
for (int i = 0; i < samples; i++)
{
float t = (float)i / sampleRate;
float sampleVal = 0f;
for (int f = 0; f < freqs.Length; f++)
{
float startTime = f * 0.1f;
if (t >= startTime)
{
float noteTime = t - startTime;
float env = Mathf.Exp(-noteTime * 4.5f);
sampleVal += Mathf.Sin(2f * Mathf.PI * freqs[f] * noteTime) * env * 0.2f;
}
}
data[i] = Mathf.Clamp(sampleVal, -0.9f, 0.9f);
}
AudioClip clip = AudioClip.Create("SuccessChime", samples, 1, sampleRate, false);
clip.SetData(data, 0);
PlayOneShot(clip, 0.5f);
}
///
/// Звук разворачивания/чтения секретной записки (шум бумаги + легкий звон)
///
public static void PlayPaperRustle()
{
int sampleRate = 44100;
int samples = (int)(sampleRate * 0.18f);
float[] data = new float[samples];
System.Random rnd = new System.Random(42);
for (int i = 0; i < samples; i++)
{
float env = 1f - (float)i / samples;
float noise = ((float)rnd.NextDouble() * 2f - 1f) * 0.15f;
float chime = Mathf.Sin(2f * Mathf.PI * 1400f * ((float)i / sampleRate)) * 0.12f;
data[i] = (noise + chime) * env;
}
AudioClip clip = AudioClip.Create("PaperRustle", samples, 1, sampleRate, false);
clip.SetData(data, 0);
PlayOneShot(clip, 0.4f);
}
private static AudioClip CreateToneClip(float frequency, float duration, float volume)
{
int sampleRate = 44100;
int samples = (int)(sampleRate * duration);
float[] data = new float[samples];
for (int i = 0; i < samples; i++)
{
float t = (float)i / sampleRate;
float env = 1f - ((float)i / samples);
data[i] = Mathf.Sin(2f * Mathf.PI * frequency * t) * env * volume;
}
AudioClip clip = AudioClip.Create($"Tone_{frequency}", samples, 1, sampleRate, false);
clip.SetData(data, 0);
return clip;
}
private static void PlayOneShot(AudioClip clip, float volumeScale)
{
if (clip == null) return;
var src = GetAudioSource();
if (src != null)
{
src.PlayOneShot(clip, volumeScale);
}
}
}