Симптом: по Bluetooth в машине с выключенным экраном радио через время замолкало.
Причины и фиксы:
- setWakeMode(C.WAKE_MODE_NETWORK) + право WAKE_LOCK — ExoPlayer держит partial
wakelock + wifilock во время игры. Без этого система усыпляла CPU/Wi-Fi при
выключенном экране → буфер пустел → поток глох (главная причина).
- onPlayerError → scheduleReconnect(): при обрыве сети (туннели, край соты) поток
пере-готавливается с нарастающей задержкой (2с→15с, до 10 попыток), а не
замолкает навсегда. Счётчик сбрасывается при успешном старте; переподключение
отменяется при ручной паузе/стопе/смене станции.
Раньше пресет эквалайзера в настройках был косметикой (лежал в DataStore, к звуку
не подключён). Теперь — реальные системные эффекты на фикс. аудиосессии плеера:
- AudioEffectsController: графический Equalizer (полосы устройства), BassBoost,
Virtualizer (объём), LoudnessEnhancer (громкость тихих, до +12 дБ). Привязка к
generateAudioSessionId() в PlayerController, переживает смену станций. Применение
в реальном времени, сохранение в DataStore (commit на отпускании слайдера).
- Отдельный экран EqualizerScreen (Настройки → ЗВУК → Эквалайзер): тумблер,
системные пресеты + «Свой», слайдеры полос (±дБ), bass/virtualizer/loudness.
- Эффекты best-effort: при отсутствии поддержки блок недоступен (null), UI скрывает.
- Убран фейковый чип-пресет Flat/Rock/Pop/Jazz/Bass.
Было: при таймере со звуком радио кроссфейдилось в шум в НАЧАЛЕ (CROSSFADE_MS=90с),
и для 15-мин таймера уже через ~1.5 мин играл полный белый шум всё оставшееся время.
Стало: радио играет почти весь таймер; в последние SOUND_OUTRO_MS (3 мин, но не
больше половины таймера) включается звук сна — радио кроссфейдится в шум, шум держится,
в самом конце затухает в тишину. Генератор шума стартует лениво (только в аутро, не
молотит весь таймер). Засыпаешь под радио, а не под резкий шум сразу.
- AudioSpectrumAnalyzer: FFT считается ТОЛЬКО когда открыт плеер (флаг active);
раньше ~86 FFT/с молотили всегда при проигрывании (даже экран выкл) — главный
пожиратель батареи. Включается из VisualizerHost через DisposableEffect.
- Спектр (45/с) собирается в leaf VisualizerHost, а не на верху PlayerBottomSheet —
весь плеер больше не рекомпозится 45 раз/сек.
- now-playing поллинг (5с) останавливается на паузе (isPlaying.collectLatest) —
раньше на паузе зря дёргали сеть каждые 5с.
- PlayerService.onDestroy отменяет serviceScope (singleton-плеер НЕ релизим).
- refreshStations (парс ~700 станций + сеть + Room) уведён на Dispatchers.IO с
главного потока (jank/ANR на старте).
- Coil ImageLoader: память 25% + диск 100МБ (обложки не перекачиваются каждую сессию).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Будильник (Settings → Будильник): несколько будильников, время, станция, дни недели,
fade-in пробуждения. AlarmManager.setAlarmClock (вне doze) + фолбэк, BootReceiver
перепланирует после перезагрузки, AlarmReceiver→PlayerService (foreground) →
PlayerController.startAlarmPlayback (нарастание громкости). Room: AlarmEntity/Dao, БД v7.
Выбор битрейта по умолчанию в Settings (Авто/Эконом/Стандарт/Высокое) → preferredBitrate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SleepSoundPlayer — процедурная генерация цветного шума через AudioTrack (розовый —
фильтр Келлета, коричневый — random walk). В таймере сна выбор звука: радио плавно
перетекает в выбранный шум (кроссфейд ≤90с), шум играет, к концу затухает — как в
спеке («Smart Sleep Fade»). В шторке таймера — чипы выбора звука.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
P0-фича из спеки. PlayerController: startSleepTimer/cancelSleepTimer — в последние
20с экспоненциальный fade-out громкости (frac^2), затем пауза + возврат громкости.
В плеере — пилюля «Таймер сна» (иконка Moon): при активном показывает остаток
M:SS акцентом. Шторка с интервалами 15/30/45/60/90/120 мин + «Выключить».
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Эквалайзер на плеере больше не декоративная синус-волна — реагирует на
реальный звук. Через TeeAudioProcessor подключаемся к декодированному PCM в
аудио-конвейере ExoPlayer (без разрешений/микрофона), считаем FFT → лог-полосы
(AudioSpectrumAnalyzer), PlayerController отдаёт спектр StateFlow'ом, LiveEqualizer
рисует столбики по уровням (с быстрым ростом/плавным спадом). Когда звука нет
(пауза/float-выход) — фолбэк на прежнюю синус-волну.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Многие станции отдают по http 301 на https; ExoPlayer по умолчанию не идёт
по кросс-протокольному редиректу и не играет их. Включён общий HTTP-источник
с setAllowCrossProtocolRedirects(true) — такие станции заиграют без правки URL.
ICY-метаданные сохраняются (обрабатываются на уровне ProgressiveMediaSource).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1) Горизонтальный свайп по области списка переключает фильтры-чипы в их
порядке ([Все]+жанры), выбранный чип автоскроллится в зону видимости.
Вертикальная прокрутка грида сохраняется.
2) У играющей станции в списке — мягкое радиальное свечение позади обложки,
которое «гуляет» (двигается центр) и вылезает из-под краёв, + эквалайзер-
бейдж в углу. Источник активной станции — PlayerController.currentStationId.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Перепроверены все 594 рабочие станции на наличие битрейт-вариантов
потока (скрипт-пробер). У 71 станции найдено по 2–4 качества
(Record-флагманы 96/64/32, zaycev 256/128/48, ВГТРК 192/128/64,
НАШЕ/Орфей/Шансон HQ и др.) — записаны в поле qualities в stations.json.
HLS (EMG) и Love (UID-привязка) корректно пропущены.
Клиент: модель StreamQuality, хранение в Room (миграция v5),
предпочтение битрейта в настройках. На экране плеера — чип текущего
качества (виден только если вариантов ≥2) и шторка «Качество звука»
со ступенями; переключение на лету без сброса now-playing, выбор
запоминается между станциями.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>