fix(alarm): cap the ring at the configured volume instead of the device max
The ring-scoped media override forced STREAM_MUSIC to the hardware maximum, so the alarm's configured percentage was applied on top of a maxed speaker: "50%" meant 50% of the phone's absolute maximum and the fade rode against that ceiling, far louder than the device- relative level users were used to. On-device logcat also showed the just_audio player emitting one buffer at volume 1.0 before the 5% pre-start took effect, a full-scale click on the maxed stream. The stream is now capped at the alarm's configured volume (still independent of the device's own level, so it rings at device-volume 0), and the player ramps from ~5% up to its full range under that cap. Perceived peak is the configured fraction of the device maximum, reached gradually; the opening click drops to the configured fraction instead of full scale.
This commit is contained in:
+8
-1
@@ -378,8 +378,15 @@ class _PaginaPrincipalState extends State<_PaginaPrincipal> {
|
|||||||
// effect for the whole ring, including fallback-WAV-only alarms that
|
// effect for the whole ring, including fallback-WAV-only alarms that
|
||||||
// never reach the station-playback branch below (Requirement:
|
// never reach the station-playback branch below (Requirement:
|
||||||
// Ring-scoped device-volume override).
|
// Ring-scoped device-volume override).
|
||||||
|
//
|
||||||
|
// The media stream is capped at the alarm's CONFIGURED volume, not the
|
||||||
|
// device max: this makes the ring independent of the device's own volume
|
||||||
|
// (audible even at 0) while keeping "50%" meaning 50% of the phone's
|
||||||
|
// maximum. The player then ramps from ~5% up to full under this cap, so
|
||||||
|
// the perceived peak is exactly the configured fraction of max, reached
|
||||||
|
// gradually — not the device-relative level, and not a full-blast max.
|
||||||
await context.read<EstadoAlarmas>().android.forzarVolumenMediaParaAlarma(
|
await context.read<EstadoAlarmas>().android.forzarVolumenMediaParaAlarma(
|
||||||
1.0,
|
alarma.volumen.clamp(0.0, 1.0),
|
||||||
);
|
);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
final emisora = alarma.emisora;
|
final emisora = alarma.emisora;
|
||||||
|
|||||||
@@ -111,7 +111,13 @@ class _PantallaAlarmaSonandoState extends State<PantallaAlarmaSonando> {
|
|||||||
|
|
||||||
void _iniciarFadeIn() {
|
void _iniciarFadeIn() {
|
||||||
_fadeInTimer?.cancel();
|
_fadeInTimer?.cancel();
|
||||||
final volumenObjetivo = widget.alarma.volumen.clamp(0.0, 1.0);
|
// The media stream is already capped at the alarm's configured volume
|
||||||
|
// (forzarVolumenMediaParaAlarma), so the player ramps up to its OWN full
|
||||||
|
// range under that cap. Perceived peak = configured% of the device max,
|
||||||
|
// reached gradually from ~5% of the cap; ramping the player to
|
||||||
|
// widget.alarma.volumen here would double-attenuate (configured x
|
||||||
|
// configured) and undershoot the level the user chose.
|
||||||
|
const volumenObjetivo = 1.0;
|
||||||
final inicio = _volumenInicialFadeIn.clamp(0.0, volumenObjetivo);
|
final inicio = _volumenInicialFadeIn.clamp(0.0, volumenObjetivo);
|
||||||
final segundosFade = widget.alarma.fadeInSegundos.clamp(0, 60);
|
final segundosFade = widget.alarma.fadeInSegundos.clamp(0, 60);
|
||||||
if (segundosFade <= 0 || volumenObjetivo <= inicio) {
|
if (segundosFade <= 0 || volumenObjetivo <= inicio) {
|
||||||
|
|||||||
@@ -205,7 +205,9 @@ void main() {
|
|||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
expect(entorno.android.detenidas, contains('ring1'));
|
expect(entorno.android.detenidas, contains('ring1'));
|
||||||
expect(entorno.audio.volumenesAplicados, [0.05, 0.85]);
|
// Player ramps to its OWN full range (1.0); the configured level is
|
||||||
|
// enforced by the media-stream cap, not by the player target.
|
||||||
|
expect(entorno.audio.volumenesAplicados, [0.05, 1.0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('si confirmar el audio con el nativo falla, el fade-in de Dart '
|
testWidgets('si confirmar el audio con el nativo falla, el fade-in de Dart '
|
||||||
@@ -225,7 +227,7 @@ void main() {
|
|||||||
// unica fuente audible, y el ring no debe quedar pegado en
|
// unica fuente audible, y el ring no debe quedar pegado en
|
||||||
// _volumenInicialFadeIn para siempre.
|
// _volumenInicialFadeIn para siempre.
|
||||||
expect(entorno.android.detenidas, isEmpty);
|
expect(entorno.android.detenidas, isEmpty);
|
||||||
expect(entorno.audio.volumenesAplicados, [0.05, 0.85]);
|
expect(entorno.audio.volumenesAplicados, [0.05, 1.0]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -247,7 +249,7 @@ void main() {
|
|||||||
);
|
);
|
||||||
expect(
|
expect(
|
||||||
entorno.audio.volumenesAplicados,
|
entorno.audio.volumenesAplicados,
|
||||||
[0.05, 0.85],
|
[0.05, 1.0],
|
||||||
reason: 'el fade-in debe arrancar tambien en el camino ya-sonando',
|
reason: 'el fade-in debe arrancar tambien en el camino ya-sonando',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user