From 900a4ad813646549927e7e493d6ba9f05064a5dd Mon Sep 17 00:00:00 2001 From: nk Date: Thu, 4 Jun 2026 18:09:40 +0300 Subject: [PATCH] =?UTF-8?q?fix(player):=20=D0=B6=D0=B8=D0=B2=D0=BE=D0=B9?= =?UTF-8?q?=20=D1=8D=D0=BA=D0=B2=D0=B0=D0=BB=D0=B0=D0=B9=D0=B7=D0=B5=D1=80?= =?UTF-8?q?=20=E2=80=94=20=D0=B0=D0=B2=D1=82=D0=BE=D0=B3=D0=B5=D0=B9=D0=BD?= =?UTF-8?q?=20+=20=D1=87=D0=B0=D1=81=D1=82=D0=BE=D1=82=D0=BD=D1=8B=D0=B9?= =?UTF-8?q?=20=D0=BC=D0=B0=D0=BF=D0=BF=D0=B8=D0=BD=D0=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Эквалайзер почти не двигался: лог-маппинг схлопывал низы в 1-2 бина, нормализация была слабой (двигались лишь правые полосы). Переделано: FFT 1024→2048 (разрешение низов), полосы по частотам 40Гц-16кГц со средним по бинам, автогейн по бегущему пику (всегда полная высота), перцептивный лифт (sqrt) + лёгкий подъём верхов. Теперь реагируют все полосы и заметно. Co-Authored-By: Claude Opus 4.8 --- .../java/com/radiola/service/AudioSpectrum.kt | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/radiola/service/AudioSpectrum.kt b/app/src/main/java/com/radiola/service/AudioSpectrum.kt index 619150d..ef2a97d 100644 --- a/app/src/main/java/com/radiola/service/AudioSpectrum.kt +++ b/app/src/main/java/com/radiola/service/AudioSpectrum.kt @@ -74,7 +74,7 @@ class AudioSpectrumAnalyzer( private val _spectrum = MutableStateFlow(FloatArray(bands)) val spectrum: StateFlow = _spectrum - private val fftSize = 1024 + private val fftSize = 2048 private val sample = FloatArray(fftSize) private val re = FloatArray(fftSize) private val im = FloatArray(fftSize) @@ -83,9 +83,14 @@ class AudioSpectrumAnalyzer( private var filled = 0 private var channelCount = 2 private var pcm16 = true + private var sampleRate = 44100 private var lastEmit = 0L + // Автогейн: бегущий пик амплитуды — чтобы столбики всегда использовали всю + // высоту независимо от громкости трека. + private var agcPeak = 1e-4f override fun flush(sampleRateHz: Int, channelCount: Int, encoding: Int) { + this.sampleRate = if (sampleRateHz > 0) sampleRateHz else 44100 this.channelCount = channelCount.coerceAtLeast(1) this.pcm16 = encoding == C.ENCODING_PCM_16BIT filled = 0 @@ -118,20 +123,39 @@ class AudioSpectrumAnalyzer( Fft.transform(re, im) val half = fftSize / 2 + val binHz = sampleRate.toFloat() / fftSize + val fMin = 40f + val fMax = 16000f + val ratio = fMax / fMin + + val raw = FloatArray(bands) + var frameMax = 0f + for (band in 0 until bands) { + // Лог-частотные полосы 40Гц..16кГц, среднее по бинам полосы. + val fLo = fMin * Math.pow(ratio.toDouble(), band.toDouble() / bands).toFloat() + val fHi = fMin * Math.pow(ratio.toDouble(), (band + 1.0) / bands).toFloat() + val binLo = (fLo / binHz).toInt().coerceIn(1, half - 1) + val binHi = (fHi / binHz).toInt().coerceIn(binLo + 1, half) + var sum = 0f + for (bin in binLo until binHi) { + sum += sqrt(re[bin] * re[bin] + im[bin] * im[bin]) + } + // Лёгкий подъём верхов (у них меньше энергии) — чтобы спектр был ровнее. + val tilt = 1f + 1.5f * (band.toFloat() / bands) + val mag = sum / (binHi - binLo) * tilt + raw[band] = mag + if (mag > frameMax) frameMax = mag + } + + // Автогейн: мгновенный рост пика, плавный спад → всегда полная высота. + agcPeak = maxOf(agcPeak * 0.94f, frameMax, 1e-4f) val out = FloatArray(bands) for (band in 0 until bands) { - // Лог-распределение полос по бинам (1..half). - val lo = Math.pow(half.toDouble(), band.toDouble() / bands).toInt().coerceIn(1, half - 1) - val hi = Math.pow(half.toDouble(), (band + 1.0) / bands).toInt().coerceIn(lo + 1, half) - var mag = 0f - for (bin in lo until hi) { - val m = sqrt(re[bin] * re[bin] + im[bin] * im[bin]) - if (m > mag) mag = m - } - val v = (ln(1f + mag * 10f) / ln(11f)).coerceIn(0f, 1f) - // Быстрый рост, плавный спад — как у настоящего эквалайзера. + // Нормируем по пику + перцептивный лифт (sqrt), чтобы тихое было видно. + val v = sqrt((raw[band] / agcPeak).coerceIn(0f, 1f)) val prev = smoothed[band] - smoothed[band] = if (v > prev) v else prev * 0.80f + v * 0.20f + // Быстрый рост, плавный спад — как у настоящего эквалайзера. + smoothed[band] = if (v > prev) v else prev * 0.78f + v * 0.22f out[band] = smoothed[band] } _spectrum.value = out