blob: 07f8dbeb8547eb11bc2e2d58a0dfe5eb5960d000 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:fidl/fidl.dart';
import 'package:fidl_fuchsia_media/fidl.dart';
import 'package:fidl_fuchsia_sys/fidl.dart';
import 'package:lib.app.dart/app.dart';
import 'package:lib.app.dart/logging.dart';
import 'package:settings_protos/audio.pb.dart' as stored_audio;
import 'package:settings_protos/setting_store_legacy.dart';
import 'package:settings_protos/setting_store_factory_legacy.dart';
/// Type for |Audio| update callbacks.
typedef UpdateCallback = void Function();
/// System audio.
class AudioLegacy {
static const double _minLevelGainDb = -60.0;
static const double _unityGainDb = 0.0;
static const double _initialGainDb = -12.0;
// These values determine what counts as a 'significant' change when deciding
// whether to call |updateCallback|.
static const double _minDbDiff = 0.006;
static const double _minPerceivedDiff = 0.0001;
final AudioProxy _audioService = new AudioProxy();
double _systemAudioGainDb = _initialGainDb;
bool _systemAudioMuted = false;
double _systemAudioPerceivedLevel = gainToLevel(_initialGainDb);
SettingStoreLegacy<stored_audio.Audio> _store;
/// Constructs an Audio object.
AudioLegacy(ServiceProvider services) {
connectToService(services, _audioService.ctrl);
_audioService.ctrl.onConnectionError = _handleConnectionError;
_audioService.ctrl.error
.then((ProxyError error) => _handleConnectionError(error: error));
_audioService.systemGainMuteChanged = _handleGainMuteChanged;
_store = new SettingStoreFactoryLegacy(services).createAudioStore()
..addlistener(_onSettingChanged)
..connect();
}
/// Called when properties have changed significantly.
UpdateCallback updateCallback;
void _onSettingChanged(stored_audio.Audio value) {
systemAudioGainDb = value.gain;
systemAudioMuted = value.muted;
}
/// Disposes this object.
void dispose() {
_audioService.ctrl.close();
}
/// Gets the system-wide audio gain in decibels. Gain values are in the range
/// -160db to 0db inclusive.
double get systemAudioGainDb => _systemAudioGainDb;
/// Sets the system-wide audio gain in db. |value| is clamped to the range
/// -160db to 0db inclusive. When gain is set to -160db, |systemAudioMuted| is
/// implicitly set to true. When gain is changed from -160db to a higher
/// value, |systemAudioMuted| is implicitly set to false.
set systemAudioGainDb(double value) {
double clampedValue = value.clamp(mutedGainDb, _unityGainDb);
if (_systemAudioGainDb == clampedValue) {
return;
}
_systemAudioGainDb = clampedValue;
_systemAudioPerceivedLevel = gainToLevel(clampedValue);
if (_systemAudioGainDb == mutedGainDb) {
_systemAudioMuted = true;
}
_audioService.setSystemGain(_systemAudioGainDb);
}
/// Gets system-wide audio muted state. |systemAudioMuted| is always true
/// when |systemAudioGainDb| is -160db.
bool get systemAudioMuted => _systemAudioMuted;
/// Sets system-wide audio muted state. Setting this value to false when
/// |systemAudioGainDb| is -160db has no effect.
set systemAudioMuted(bool value) {
bool muted = value || _systemAudioGainDb == mutedGainDb;
if (_systemAudioMuted == muted) {
return;
}
_systemAudioMuted = muted;
_audioService.setSystemMute(_systemAudioMuted);
_persistUserSetting();
}
void _persistUserSetting() {
final stored_audio.Audio audio = new stored_audio.Audio()
..clear()
..muted = _systemAudioMuted
..gain = _systemAudioGainDb;
_store.commit(audio);
}
/// Gets the perceived system-wide audio level in the range [0,1]. This value
/// is intended to be used for volume sliders. If there is no separate mute
/// control, use (systemAudioMuted ? 0.0 : systemAudioPerceivedLevel).
double get systemAudioPerceivedLevel => _systemAudioPerceivedLevel;
/// Sets the perceived system-wide audio level in the range [0,1]. When this
/// property is set to 0.0, |systemAudioGainDb| is set to -160db.
set systemAudioPerceivedLevel(double value) {
_systemAudioPerceivedLevel = value.clamp(0.0, 1.0);
_systemAudioGainDb = levelToGain(_systemAudioPerceivedLevel);
_persistUserSetting();
_audioService.setSystemGain(_systemAudioGainDb);
}
// Handles a status update from the audio service. Call with
// kInitialStatus, null to initiate status updates.
void _handleGainMuteChanged(double gainDb, bool muted) {
bool callUpdate = _systemAudioMuted != muted ||
(_systemAudioGainDb - gainDb).abs() > _minDbDiff;
_systemAudioGainDb = gainDb;
_systemAudioMuted = muted;
double newPerceivedLevel = gainToLevel(_systemAudioGainDb);
if ((_systemAudioPerceivedLevel - newPerceivedLevel).abs() >
_minPerceivedDiff) {
_systemAudioPerceivedLevel = newPerceivedLevel;
callUpdate = true;
}
if (callUpdate && updateCallback != null) {
updateCallback();
}
}
/// Handles connection error to the audio service.
void _handleConnectionError({ProxyError error}) {
log.severe('Unable to connect to audio service', error);
}
void setRoutingPolicy(AudioOutputRoutingPolicy policy) {
_audioService.setRoutingPolicy(policy);
}
/// Converts a gain in db to an audio 'level' in the range 0.0 to 1.0
/// inclusive.
static double gainToLevel(double gainDb) {
if (gainDb <= _minLevelGainDb) {
return 0.0;
}
if (gainDb >= _unityGainDb) {
return 1.0;
}
return 1.0 - gainDb / _minLevelGainDb;
}
/// Converts an audio 'level' in the range 0.0 to 1.0 inclusive to a gain in
/// db.
static double levelToGain(double level) {
if (level <= 0.0) {
return mutedGainDb;
}
if (level >= 1.0) {
return _unityGainDb;
}
return (1.0 - level) * _minLevelGainDb;
}
}