feat(eq): add per-device equalizer with 4-level preset resolution
Introduce multi-device EQ support allowing each audio output device (built-in speaker, wired, USB, individual Bluetooth by MAC) to have its own equalizer preset, combined with existing per-station presets for a full station×device matrix. - Add DispositivoAudio model and ServicioDispositivoAudio interface - Add Android platform channel (AudioDeviceCallback) for device detection - Add iOS AudioDevicesPlugin (AVAudioSession route tracking) - Extend ServicioEcualizador with device and matrix persistence keys - Implement 4-level resolution: matrix > station > device > global - Add advanced EQ settings section with feature toggle (off by default) - Extend export/import to v3 with backward compatibility - 184 tests passing, zero analyzer issues
This commit is contained in:
+12
-1
@@ -616,5 +616,16 @@
|
||||
"stationCount": "{count, plural, =1{1 station} other{{count} stations}}",
|
||||
"alarmIconLabel": "Musical alarm",
|
||||
"vacationIconLabel": "Vacation mode",
|
||||
"streamUrlHint": "https://stream.example.com:8000/radio"
|
||||
"streamUrlHint": "https://stream.example.com:8000/radio",
|
||||
"advancedEqSectionTitle": "Advanced Equalization Options",
|
||||
"advancedEqEnableToggle": "Enable per-device EQ",
|
||||
"advancedEqEnableToggleSubtitle": "Automatically apply a different EQ preset when your audio output changes.",
|
||||
"advancedEqKnownDevicesTitle": "Known audio devices",
|
||||
"advancedEqKnownDevicesEmpty": "No audio devices recorded yet. Connect a device to get started.",
|
||||
"advancedEqDevicePresetLabel": "Preset: {presetName}",
|
||||
"@advancedEqDevicePresetLabel": {
|
||||
"placeholders": {
|
||||
"presetName": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,5 +579,16 @@
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"advancedEqSectionTitle": "Opciones avanzadas de ecualización",
|
||||
"advancedEqEnableToggle": "Activar EQ por dispositivo",
|
||||
"advancedEqEnableToggleSubtitle": "Aplica automáticamente un preset de EQ distinto al cambiar la salida de audio.",
|
||||
"advancedEqKnownDevicesTitle": "Dispositivos de audio conocidos",
|
||||
"advancedEqKnownDevicesEmpty": "No hay dispositivos registrados. Conectá uno para empezar.",
|
||||
"advancedEqDevicePresetLabel": "Preset: {presetName}",
|
||||
"@advancedEqDevicePresetLabel": {
|
||||
"placeholders": {
|
||||
"presetName": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2269,6 +2269,42 @@ abstract class AppLocalizations {
|
||||
/// In es, this message translates to:
|
||||
/// **'https://stream.example.com:8000/radio'**
|
||||
String get streamUrlHint;
|
||||
|
||||
/// No description provided for @advancedEqSectionTitle.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Opciones avanzadas de ecualización'**
|
||||
String get advancedEqSectionTitle;
|
||||
|
||||
/// No description provided for @advancedEqEnableToggle.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Activar EQ por dispositivo'**
|
||||
String get advancedEqEnableToggle;
|
||||
|
||||
/// No description provided for @advancedEqEnableToggleSubtitle.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Aplica automáticamente un preset de EQ distinto al cambiar la salida de audio.'**
|
||||
String get advancedEqEnableToggleSubtitle;
|
||||
|
||||
/// No description provided for @advancedEqKnownDevicesTitle.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Dispositivos de audio conocidos'**
|
||||
String get advancedEqKnownDevicesTitle;
|
||||
|
||||
/// No description provided for @advancedEqKnownDevicesEmpty.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'No hay dispositivos registrados. Conectá uno para empezar.'**
|
||||
String get advancedEqKnownDevicesEmpty;
|
||||
|
||||
/// No description provided for @advancedEqDevicePresetLabel.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Preset: {presetName}'**
|
||||
String advancedEqDevicePresetLabel(Object presetName);
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
|
||||
@@ -1225,4 +1225,26 @@ class AppLocalizationsAr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get streamUrlHint => 'https://stream.example.com:8000/radio';
|
||||
|
||||
@override
|
||||
String get advancedEqSectionTitle => 'Opciones avanzadas de ecualización';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggle => 'Activar EQ por dispositivo';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggleSubtitle =>
|
||||
'Aplica automáticamente un preset de EQ distinto al cambiar la salida de audio.';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesTitle => 'Dispositivos de audio conocidos';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesEmpty =>
|
||||
'No hay dispositivos registrados. Conectá uno para empezar.';
|
||||
|
||||
@override
|
||||
String advancedEqDevicePresetLabel(Object presetName) {
|
||||
return 'Preset: $presetName';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1232,4 +1232,26 @@ class AppLocalizationsBn extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get streamUrlHint => 'https://stream.example.com:8000/radio';
|
||||
|
||||
@override
|
||||
String get advancedEqSectionTitle => 'Opciones avanzadas de ecualización';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggle => 'Activar EQ por dispositivo';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggleSubtitle =>
|
||||
'Aplica automáticamente un preset de EQ distinto al cambiar la salida de audio.';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesTitle => 'Dispositivos de audio conocidos';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesEmpty =>
|
||||
'No hay dispositivos registrados. Conectá uno para empezar.';
|
||||
|
||||
@override
|
||||
String advancedEqDevicePresetLabel(Object presetName) {
|
||||
return 'Preset: $presetName';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1242,4 +1242,26 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get streamUrlHint => 'https://stream.example.com:8000/radio';
|
||||
|
||||
@override
|
||||
String get advancedEqSectionTitle => 'Opciones avanzadas de ecualización';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggle => 'Activar EQ por dispositivo';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggleSubtitle =>
|
||||
'Aplica automáticamente un preset de EQ distinto al cambiar la salida de audio.';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesTitle => 'Dispositivos de audio conocidos';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesEmpty =>
|
||||
'No hay dispositivos registrados. Conectá uno para empezar.';
|
||||
|
||||
@override
|
||||
String advancedEqDevicePresetLabel(Object presetName) {
|
||||
return 'Preset: $presetName';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1228,4 +1228,26 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get streamUrlHint => 'https://stream.example.com:8000/radio';
|
||||
|
||||
@override
|
||||
String get advancedEqSectionTitle => 'Advanced Equalization Options';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggle => 'Enable per-device EQ';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggleSubtitle =>
|
||||
'Automatically apply a different EQ preset when your audio output changes.';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesTitle => 'Known audio devices';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesEmpty =>
|
||||
'No audio devices recorded yet. Connect a device to get started.';
|
||||
|
||||
@override
|
||||
String advancedEqDevicePresetLabel(Object presetName) {
|
||||
return 'Preset: $presetName';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1237,4 +1237,26 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get streamUrlHint => 'https://stream.example.com:8000/radio';
|
||||
|
||||
@override
|
||||
String get advancedEqSectionTitle => 'Opciones avanzadas de ecualización';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggle => 'Activar EQ por dispositivo';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggleSubtitle =>
|
||||
'Aplica automáticamente un preset de EQ distinto al cambiar la salida de audio.';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesTitle => 'Dispositivos de audio conocidos';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesEmpty =>
|
||||
'No hay dispositivos registrados. Conectá uno para empezar.';
|
||||
|
||||
@override
|
||||
String advancedEqDevicePresetLabel(Object presetName) {
|
||||
return 'Preset: $presetName';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1247,4 +1247,26 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get streamUrlHint => 'https://stream.example.com:8000/radio';
|
||||
|
||||
@override
|
||||
String get advancedEqSectionTitle => 'Opciones avanzadas de ecualización';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggle => 'Activar EQ por dispositivo';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggleSubtitle =>
|
||||
'Aplica automáticamente un preset de EQ distinto al cambiar la salida de audio.';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesTitle => 'Dispositivos de audio conocidos';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesEmpty =>
|
||||
'No hay dispositivos registrados. Conectá uno para empezar.';
|
||||
|
||||
@override
|
||||
String advancedEqDevicePresetLabel(Object presetName) {
|
||||
return 'Preset: $presetName';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1231,4 +1231,26 @@ class AppLocalizationsHi extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get streamUrlHint => 'https://stream.example.com:8000/radio';
|
||||
|
||||
@override
|
||||
String get advancedEqSectionTitle => 'Opciones avanzadas de ecualización';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggle => 'Activar EQ por dispositivo';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggleSubtitle =>
|
||||
'Aplica automáticamente un preset de EQ distinto al cambiar la salida de audio.';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesTitle => 'Dispositivos de audio conocidos';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesEmpty =>
|
||||
'No hay dispositivos registrados. Conectá uno para empezar.';
|
||||
|
||||
@override
|
||||
String advancedEqDevicePresetLabel(Object presetName) {
|
||||
return 'Preset: $presetName';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1236,4 +1236,26 @@ class AppLocalizationsId extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get streamUrlHint => 'https://stream.example.com:8000/radio';
|
||||
|
||||
@override
|
||||
String get advancedEqSectionTitle => 'Opciones avanzadas de ecualización';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggle => 'Activar EQ por dispositivo';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggleSubtitle =>
|
||||
'Aplica automáticamente un preset de EQ distinto al cambiar la salida de audio.';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesTitle => 'Dispositivos de audio conocidos';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesEmpty =>
|
||||
'No hay dispositivos registrados. Conectá uno para empezar.';
|
||||
|
||||
@override
|
||||
String advancedEqDevicePresetLabel(Object presetName) {
|
||||
return 'Preset: $presetName';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1242,4 +1242,26 @@ class AppLocalizationsIt extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get streamUrlHint => 'https://stream.example.com:8000/radio';
|
||||
|
||||
@override
|
||||
String get advancedEqSectionTitle => 'Opciones avanzadas de ecualización';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggle => 'Activar EQ por dispositivo';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggleSubtitle =>
|
||||
'Aplica automáticamente un preset de EQ distinto al cambiar la salida de audio.';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesTitle => 'Dispositivos de audio conocidos';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesEmpty =>
|
||||
'No hay dispositivos registrados. Conectá uno para empezar.';
|
||||
|
||||
@override
|
||||
String advancedEqDevicePresetLabel(Object presetName) {
|
||||
return 'Preset: $presetName';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1192,4 +1192,26 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get streamUrlHint => 'https://stream.example.com:8000/radio';
|
||||
|
||||
@override
|
||||
String get advancedEqSectionTitle => 'Opciones avanzadas de ecualización';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggle => 'Activar EQ por dispositivo';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggleSubtitle =>
|
||||
'Aplica automáticamente un preset de EQ distinto al cambiar la salida de audio.';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesTitle => 'Dispositivos de audio conocidos';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesEmpty =>
|
||||
'No hay dispositivos registrados. Conectá uno para empezar.';
|
||||
|
||||
@override
|
||||
String advancedEqDevicePresetLabel(Object presetName) {
|
||||
return 'Preset: $presetName';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1234,4 +1234,26 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get streamUrlHint => 'https://stream.example.com:8000/radio';
|
||||
|
||||
@override
|
||||
String get advancedEqSectionTitle => 'Opciones avanzadas de ecualización';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggle => 'Activar EQ por dispositivo';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggleSubtitle =>
|
||||
'Aplica automáticamente un preset de EQ distinto al cambiar la salida de audio.';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesTitle => 'Dispositivos de audio conocidos';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesEmpty =>
|
||||
'No hay dispositivos registrados. Conectá uno para empezar.';
|
||||
|
||||
@override
|
||||
String advancedEqDevicePresetLabel(Object presetName) {
|
||||
return 'Preset: $presetName';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1238,4 +1238,26 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get streamUrlHint => 'https://stream.example.com:8000/radio';
|
||||
|
||||
@override
|
||||
String get advancedEqSectionTitle => 'Opciones avanzadas de ecualización';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggle => 'Activar EQ por dispositivo';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggleSubtitle =>
|
||||
'Aplica automáticamente un preset de EQ distinto al cambiar la salida de audio.';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesTitle => 'Dispositivos de audio conocidos';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesEmpty =>
|
||||
'No hay dispositivos registrados. Conectá uno para empezar.';
|
||||
|
||||
@override
|
||||
String advancedEqDevicePresetLabel(Object presetName) {
|
||||
return 'Preset: $presetName';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1187,4 +1187,26 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get streamUrlHint => 'https://stream.example.com:8000/radio';
|
||||
|
||||
@override
|
||||
String get advancedEqSectionTitle => 'Opciones avanzadas de ecualización';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggle => 'Activar EQ por dispositivo';
|
||||
|
||||
@override
|
||||
String get advancedEqEnableToggleSubtitle =>
|
||||
'Aplica automáticamente un preset de EQ distinto al cambiar la salida de audio.';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesTitle => 'Dispositivos de audio conocidos';
|
||||
|
||||
@override
|
||||
String get advancedEqKnownDevicesEmpty =>
|
||||
'No hay dispositivos registrados. Conectá uno para empezar.';
|
||||
|
||||
@override
|
||||
String advancedEqDevicePresetLabel(Object presetName) {
|
||||
return 'Preset: $presetName';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user