fix(recordings): не зависать плееру записи; меньше задержка обложки
Bug1: плеер записи (singleton ExoPlayer) не глушился при закрытии шторки и уходе с экрана → аудио-сирота без управления, запуск радио конфликтовал. Теперь воспроизведение записи останавливается на onDismiss и onDispose экрана записей, а старт радио глушит плеер записи (взаимоисключение). Bug2: обложка/трек на открытом плеере обновлялись с задержкой при записи. Эмиссия спектра ограничена ~45/с (было ~86/с) — меньше перегруз перерисовки; поллинг now-playing в захвате маркеров ускорен 15с→8с (точнее тайм-коды). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -97,7 +97,7 @@ class RecordingRepositoryImpl @Inject constructor(
|
|||||||
launch {
|
launch {
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
try { nowPlayingRepository.refreshNowPlaying() } catch (_: Exception) {}
|
try { nowPlayingRepository.refreshNowPlaying() } catch (_: Exception) {}
|
||||||
delay(15_000)
|
delay(8_000) // чаще — точнее тайм-коды треков в записи
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nowPlayingRepository.getNowPlaying(station.id)
|
nowPlayingRepository.getNowPlaying(station.id)
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ class AudioSpectrumAnalyzer(
|
|||||||
private var channelCount = 2
|
private var channelCount = 2
|
||||||
private var pcm16 = true
|
private var pcm16 = true
|
||||||
private var sampleRate = 44100
|
private var sampleRate = 44100
|
||||||
|
private var lastEmit = 0L
|
||||||
// Автогейн: бегущий пик амплитуды — чтобы столбики всегда использовали всю
|
// Автогейн: бегущий пик амплитуды — чтобы столбики всегда использовали всю
|
||||||
// высоту независимо от громкости трека.
|
// высоту независимо от громкости трека.
|
||||||
private var agcPeak = 1e-4f
|
private var agcPeak = 1e-4f
|
||||||
@@ -159,6 +160,12 @@ class AudioSpectrumAnalyzer(
|
|||||||
smoothed[band] = if (v > prev) v else prev * 0.55f + v * 0.45f
|
smoothed[band] = if (v > prev) v else prev * 0.55f + v * 0.45f
|
||||||
out[band] = smoothed[band]
|
out[band] = smoothed[band]
|
||||||
}
|
}
|
||||||
|
// Считаем сглаживание каждый хоп (плавность), но эмитим в UI ~45/с, чтобы
|
||||||
|
// не перегружать перерисовку плеера.
|
||||||
|
val now = System.nanoTime()
|
||||||
|
if (now - lastEmit >= 22_000_000L) {
|
||||||
|
lastEmit = now
|
||||||
_spectrum.value = out
|
_spectrum.value = out
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ class PlayerViewModel @Inject constructor(
|
|||||||
private val settingsRepository: SettingsRepository,
|
private val settingsRepository: SettingsRepository,
|
||||||
private val recordingRepository: RecordingRepository,
|
private val recordingRepository: RecordingRepository,
|
||||||
private val pushHistoryUseCase: PushHistoryUseCase,
|
private val pushHistoryUseCase: PushHistoryUseCase,
|
||||||
private val loveStreamResolver: com.radiola.data.remote.LoveStreamResolver
|
private val loveStreamResolver: com.radiola.data.remote.LoveStreamResolver,
|
||||||
|
private val recordingPlaybackController: com.radiola.service.RecordingPlaybackController
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
val isPlaying: StateFlow<Boolean> = playerController.isPlaying
|
val isPlaying: StateFlow<Boolean> = playerController.isPlaying
|
||||||
@@ -98,6 +99,9 @@ class PlayerViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun play(station: Station, playlist: List<Station>? = null) {
|
fun play(station: Station, playlist: List<Station>? = null) {
|
||||||
|
// Глушим плеер записи, если он играл — иначе два ExoPlayer'а конфликтуют
|
||||||
|
// (радио не стартует, запись зависает без управления).
|
||||||
|
recordingPlaybackController.stop()
|
||||||
_currentStation.value = station
|
_currentStation.value = station
|
||||||
_currentTrack.value = null
|
_currentTrack.value = null
|
||||||
_playlist.value = playlist ?: _stations.value
|
_playlist.value = playlist ?: _stations.value
|
||||||
|
|||||||
@@ -48,6 +48,13 @@ fun RecordingsScreen(
|
|||||||
val isRecording by viewModel.isRecording.collectAsState()
|
val isRecording by viewModel.isRecording.collectAsState()
|
||||||
val colors = RadiolaTheme.colors
|
val colors = RadiolaTheme.colors
|
||||||
var playing by remember { mutableStateOf<Recording?>(null) }
|
var playing by remember { mutableStateOf<Recording?>(null) }
|
||||||
|
// Плеер записи — singleton-контроллер; держим его VM здесь, чтобы корректно
|
||||||
|
// ОСТАНОВИТЬ воспроизведение при закрытии шторки и уходе с экрана (иначе
|
||||||
|
// аудио продолжает играть без UI и конфликтует с радио).
|
||||||
|
val recPlayerVm: RecordingPlayerViewModel = hiltViewModel()
|
||||||
|
DisposableEffect(Unit) {
|
||||||
|
onDispose { recPlayerVm.close() }
|
||||||
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -133,14 +140,14 @@ fun RecordingsScreen(
|
|||||||
// налезает на системную навигацию.
|
// налезает на системную навигацию.
|
||||||
playing?.let { rec ->
|
playing?.let { rec ->
|
||||||
ModalBottomSheet(
|
ModalBottomSheet(
|
||||||
onDismissRequest = { playing = null },
|
onDismissRequest = { recPlayerVm.close(); playing = null },
|
||||||
sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
|
sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
|
||||||
containerColor = colors.bgBase
|
containerColor = colors.bgBase
|
||||||
) {
|
) {
|
||||||
RecordingPlayerSheet(
|
RecordingPlayerSheet(
|
||||||
recording = rec,
|
recording = rec,
|
||||||
onDismiss = { playing = null },
|
onDismiss = { recPlayerVm.close(); playing = null },
|
||||||
viewModel = hiltViewModel()
|
viewModel = recPlayerVm
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user