Revert "Revert "audiopolicy: moves Stream Volume Curves to Engine""
This reverts commit efdbe3bfeff7c150d99900fddca005d48048f188.
Test: make
diff --git a/services/audiopolicy/engine/common/include/EngineBase.h b/services/audiopolicy/engine/common/include/EngineBase.h
index 5c33fb3..bc027e2 100644
--- a/services/audiopolicy/engine/common/include/EngineBase.h
+++ b/services/audiopolicy/engine/common/include/EngineBase.h
@@ -19,6 +19,7 @@
#include <EngineConfig.h>
#include <AudioPolicyManagerInterface.h>
#include <ProductStrategy.h>
+#include <StreamVolumeCurves.h>
namespace android {
namespace audio_policy {
@@ -67,6 +68,10 @@
status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) const override;
+ VolumeCurves *getVolumeCurvesForAttributes(const audio_attributes_t &attr) override;
+
+ VolumeCurves *getVolumeCurvesForStreamType(audio_stream_type_t stream) override;
+
void dump(String8 *dst) const override;
@@ -87,7 +92,16 @@
return is_state_in_call(getPhoneState());
}
-private:
+ VolumeSource toVolumeSource(audio_stream_type_t stream) const
+ {
+ return static_cast<VolumeSource>(stream);
+ }
+
+ status_t switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst);
+
+ status_t restoreOriginVolumeCurve(audio_stream_type_t stream);
+
+ private:
AudioPolicyManagerObserver *mApmObserver = nullptr;
ProductStrategyMap mProductStrategies;
@@ -95,6 +109,8 @@
/** current forced use configuration. */
audio_policy_forced_cfg_t mForceUse[AUDIO_POLICY_FORCE_USE_CNT] = {};
+
+ StreamVolumeCurves mStreamVolumeCurves;
};
} // namespace audio_policy
diff --git a/services/audiopolicy/engine/common/include/StreamVolumeCurves.h b/services/audiopolicy/engine/common/include/StreamVolumeCurves.h
new file mode 100644
index 0000000..5b0b7d6
--- /dev/null
+++ b/services/audiopolicy/engine/common/include/StreamVolumeCurves.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <VolumeCurve.h>
+#include <map>
+
+namespace android {
+
+class StreamVolumeCurves
+{
+public:
+ StreamVolumeCurves() = default;
+
+ /**
+ * @brief switchVolumeCurve control API for Engine, allows to switch the volume curves
+ * from one stream type to another.
+ * @param src source stream type
+ * @param dst destination stream type
+ */
+ status_t switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst)
+ {
+ if (!hasCurvesFor(streamSrc) || !hasCurvesFor(streamDst)) {
+ ALOGE("%s: No curves defined for streams %d %d", __FUNCTION__, streamSrc, streamDst);
+ return NO_INIT;
+ }
+ const VolumeCurves &sourceCurves = getCurvesFor(streamSrc);
+ VolumeCurves &dstCurves = editCurvesFor(streamDst);
+ return dstCurves.switchCurvesFrom(sourceCurves);
+ }
+ void dump(String8 *dst, int spaces = 0) const;
+
+ void add(const VolumeCurves &curves, audio_stream_type_t streamType)
+ {
+ mCurves.emplace(streamType, curves);
+ }
+
+ bool hasCurvesFor(audio_stream_type_t stream)
+ {
+ return mCurves.find(stream) != end(mCurves);
+ }
+
+ VolumeCurves &editCurvesFor(audio_stream_type_t stream)
+ {
+ ALOG_ASSERT(mCurves.find(stream) != end(mCurves), "Invalid stream type for Volume Curve");
+ return mCurves[stream];
+ }
+ const VolumeCurves &getCurvesFor(audio_stream_type_t stream) const
+ {
+ ALOG_ASSERT(mCurves.find(stream) != end(mCurves), "Invalid stream type for Volume Curve");
+ return mCurves.at(stream);
+ }
+ /**
+ * @brief getVolumeCurvesForStream
+ * @param stream type for which the volume curves interface is requested
+ * @return the VolumeCurves for a given stream type.
+ */
+ VolumeCurves &getVolumeCurvesForStream(audio_stream_type_t stream)
+ {
+ ALOG_ASSERT(mCurves.find(stream) != end(mCurves), "Invalid stream type for Volume Curve");
+ return mCurves[stream];
+ }
+ /**
+ * @brief restoreOriginVolumeCurve helper control API for engine to restore the original volume
+ * curves for a given stream type
+ * @param stream for which the volume curves will be restored.
+ */
+ status_t restoreOriginVolumeCurve(audio_stream_type_t stream)
+ {
+ if (!hasCurvesFor(stream)) {
+ ALOGE("%s: No curves defined for streams", __FUNCTION__);
+ return NO_INIT;
+ }
+ return switchVolumeCurve(stream, stream);
+ }
+
+private:
+ std::map<audio_stream_type_t, VolumeCurves> mCurves;
+};
+
+} // namespace android
diff --git a/services/audiopolicy/engine/common/include/VolumeCurve.h b/services/audiopolicy/engine/common/include/VolumeCurve.h
new file mode 100644
index 0000000..c2a1da7
--- /dev/null
+++ b/services/audiopolicy/engine/common/include/VolumeCurve.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "IVolumeCurves.h"
+#include <policy.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/SortedVector.h>
+#include <utils/KeyedVector.h>
+#include <system/audio.h>
+#include <cutils/config_utils.h>
+#include <string>
+#include <map>
+#include <utility>
+
+namespace android {
+
+struct CurvePoint
+{
+ CurvePoint() {}
+ CurvePoint(int index, int attenuationInMb) :
+ mIndex(index), mAttenuationInMb(attenuationInMb) {}
+ uint32_t mIndex;
+ int mAttenuationInMb;
+};
+
+inline bool operator< (const CurvePoint &lhs, const CurvePoint &rhs)
+{
+ return lhs.mIndex < rhs.mIndex;
+}
+
+// A volume curve for a given use case and device category
+// It contains of list of points of this curve expressing the attenuation in Millibels for
+// a given volume index from 0 to 100
+class VolumeCurve : public RefBase
+{
+public:
+ VolumeCurve(device_category device) : mDeviceCategory(device) {}
+
+ void add(const CurvePoint &point) { mCurvePoints.add(point); }
+
+ float volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) const;
+
+ void dump(String8 *dst, int spaces = 0, bool curvePoints = false) const;
+
+ device_category getDeviceCategory() const { return mDeviceCategory; }
+
+private:
+ const device_category mDeviceCategory;
+ SortedVector<CurvePoint> mCurvePoints;
+};
+
+// Volume Curves for a given use case indexed by device category
+class VolumeCurves : public KeyedVector<device_category, sp<VolumeCurve> >,
+ public IVolumeCurves
+{
+public:
+ VolumeCurves(int indexMin = 0, int indexMax = 100) :
+ mIndexMin(indexMin), mIndexMax(indexMax), mStream(AUDIO_STREAM_DEFAULT)
+ {
+ addCurrentVolumeIndex(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, 0);
+ }
+ VolumeCurves(audio_stream_type_t stream, int indexMin, int indexMax) :
+ mIndexMin(indexMin), mIndexMax(indexMax), mStream(stream)
+ {
+ addCurrentVolumeIndex(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, 0);
+ }
+
+ // Once XML has been parsed, must be call first to sanity check table and initialize indexes
+ virtual status_t initVolume(int indexMin, int indexMax)
+ {
+ mIndexMin = indexMin;
+ mIndexMax = indexMax;
+ return NO_ERROR;
+ }
+
+ sp<VolumeCurve> getCurvesFor(device_category device) const
+ {
+ if (indexOfKey(device) < 0) {
+ return 0;
+ }
+ return valueFor(device);
+ }
+
+ virtual int getVolumeIndex(audio_devices_t device) const
+ {
+ device = Volume::getDeviceForVolume(device);
+ // there is always a valid entry for AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME
+ if (mIndexCur.find(device) == end(mIndexCur)) {
+ device = AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME;
+ }
+ return mIndexCur.at(device);
+ }
+
+ virtual bool canBeMuted() const { return mCanBeMuted; }
+ virtual void clearCurrentVolumeIndex() { mIndexCur.clear(); }
+ void addCurrentVolumeIndex(audio_devices_t device, int index) override
+ {
+ mIndexCur[device] = index;
+ }
+
+ int getVolumeIndexMin() const { return mIndexMin; }
+
+ int getVolumeIndexMax() const { return mIndexMax; }
+
+ bool hasVolumeIndexForDevice(audio_devices_t device) const
+ {
+ device = Volume::getDeviceForVolume(device);
+ return mIndexCur.find(device) != end(mIndexCur);
+ }
+
+ status_t switchCurvesFrom(const VolumeCurves &referenceCurves)
+ {
+ if (size() != referenceCurves.size()) {
+ ALOGE("%s! device category not aligned, cannot switch", __FUNCTION__);
+ return BAD_TYPE;
+ }
+ for (size_t index = 0; index < size(); index++) {
+ device_category cat = keyAt(index);
+ setVolumeCurve(cat, referenceCurves.getOriginVolumeCurve(cat));
+ }
+ return NO_ERROR;
+ }
+ status_t restoreOriginVolumeCurve()
+ {
+ return switchCurvesFrom(*this);
+ }
+
+ const sp<VolumeCurve> getOriginVolumeCurve(device_category deviceCategory) const
+ {
+ ALOG_ASSERT(mOriginVolumeCurves.indexOfKey(deviceCategory) >= 0, "Invalid device category");
+ return mOriginVolumeCurves.valueFor(deviceCategory);
+ }
+ void setVolumeCurve(device_category deviceCategory, const sp<VolumeCurve> &volumeCurve)
+ {
+ ALOG_ASSERT(indexOfKey(deviceCategory) >= 0, "Invalid device category for Volume Curve");
+ replaceValueFor(deviceCategory, volumeCurve);
+ }
+
+ ssize_t add(const sp<VolumeCurve> &volumeCurve)
+ {
+ device_category deviceCategory = volumeCurve->getDeviceCategory();
+ ssize_t index = indexOfKey(deviceCategory);
+ if (index < 0) {
+ // Keep track of original Volume Curves per device category in order to switch curves.
+ mOriginVolumeCurves.add(deviceCategory, volumeCurve);
+ return KeyedVector::add(deviceCategory, volumeCurve);
+ }
+ return index;
+ }
+
+ virtual float volIndexToDb(device_category deviceCat, int indexInUi) const
+ {
+ sp<VolumeCurve> vc = getCurvesFor(deviceCat);
+ if (vc != 0) {
+ return vc->volIndexToDb(indexInUi, mIndexMin, mIndexMax);
+ } else {
+ ALOGE("Invalid device category %d for Volume Curve", deviceCat);
+ return 0.0f;
+ }
+ }
+
+ audio_stream_type_t getStreamType() const { return mStream; }
+
+ void dump(String8 *dst, int spaces = 0, bool curvePoints = false) const override;
+
+private:
+ KeyedVector<device_category, sp<VolumeCurve> > mOriginVolumeCurves;
+ std::map<audio_devices_t, int> mIndexCur; /**< current volume index per device. */
+ /*const*/ int mIndexMin; /**< min volume index. */
+ /*const*/ int mIndexMax; /**< max volume index. */
+ const bool mCanBeMuted = true; /**< true is the stream can be muted. */
+
+ const audio_stream_type_t mStream; /**< Keep it for legacy. */
+};
+
+} // namespace android
diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp
index 755f2a8..2449b61 100644
--- a/services/audiopolicy/engine/common/src/EngineBase.cpp
+++ b/services/audiopolicy/engine/common/src/EngineBase.cpp
@@ -55,8 +55,10 @@
if (!is_state_in_call(oldState) && is_state_in_call(state)) {
ALOGV(" Entering call in setPhoneState()");
+ switchVolumeCurve(AUDIO_STREAM_VOICE_CALL, AUDIO_STREAM_DTMF);
} else if (is_state_in_call(oldState) && !is_state_in_call(state)) {
ALOGV(" Exiting call in setPhoneState()");
+ restoreOriginVolumeCurve(AUDIO_STREAM_DTMF);
}
return NO_ERROR;
}
@@ -108,6 +110,31 @@
productStrategies[strategyId] = strategy;
}
};
+ auto loadVolumeCurves = [](const auto &configVolumes, auto &streamVolumeCollection) {
+ for (auto &configVolume : configVolumes) {
+ audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT;
+ if (configVolume.stream.empty() ||
+ !StreamTypeConverter::fromString(configVolume.stream, streamType)) {
+ ALOGE("%s: Invalid stream type", __FUNCTION__);
+ continue;
+ }
+ VolumeCurves volumeCurves(streamType, configVolume.indexMin, configVolume.indexMax);
+ for (auto &configCurve : configVolume.volumeCurves) {
+ device_category deviceCategory = DEVICE_CATEGORY_SPEAKER;
+ if (!DeviceCategoryConverter::fromString(configCurve.deviceCategory,
+ deviceCategory)) {
+ ALOGE("%s: Invalid %s", __FUNCTION__, configCurve.deviceCategory.c_str());
+ continue;
+ }
+ sp<VolumeCurve> curve = new VolumeCurve(deviceCategory);
+ for (auto &point : configCurve.curvePoints) {
+ curve->add({point.index, point.attenuationInMb});
+ }
+ volumeCurves.add(curve);
+ }
+ streamVolumeCollection.add(volumeCurves, streamType);
+ }
+ };
auto result = engineConfig::parse();
if (result.parsedConfig == nullptr) {
@@ -116,6 +143,7 @@
}
ALOGE_IF(result.nbSkippedElement != 0, "skipped %zu elements", result.nbSkippedElement);
loadProductStrategies(result.parsedConfig->productStrategies, mProductStrategies);
+ loadVolumeCurves(result.parsedConfig->volumeGroups, mStreamVolumeCurves);
return result;
}
@@ -173,9 +201,30 @@
return NO_ERROR;
}
+VolumeCurves *EngineBase::getVolumeCurvesForAttributes(const audio_attributes_t &attr)
+{
+ return &mStreamVolumeCurves.getVolumeCurvesForStream(getStreamTypeForAttributes(attr));
+}
+
+VolumeCurves *EngineBase::getVolumeCurvesForStreamType(audio_stream_type_t stream)
+{
+ return &mStreamVolumeCurves.getVolumeCurvesForStream(stream);
+}
+
+status_t EngineBase::switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst)
+{
+ return mStreamVolumeCurves.switchVolumeCurve(streamSrc, streamDst);;
+}
+
+status_t EngineBase::restoreOriginVolumeCurve(audio_stream_type_t stream)
+{
+ return mStreamVolumeCurves.restoreOriginVolumeCurve(stream);
+}
+
void EngineBase::dump(String8 *dst) const
{
mProductStrategies.dump(dst, 2);
+ mStreamVolumeCurves.dump(dst, 2);
}
} // namespace audio_policy
diff --git a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
index 3940c0c..86a59b0 100644
--- a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
+++ b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
@@ -131,10 +131,131 @@
}
};
+const engineConfig::VolumeGroups gVolumeGroups = {
+ {"voice_call", "AUDIO_STREAM_VOICE_CALL", 1, 10,
+ {
+ {"DEVICE_CATEGORY_HEADSET", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } },
+ {"DEVICE_CATEGORY_SPEAKER", { {0, -2400}, {33, -1600}, {66, -800}, {100, 0} } },
+ {"DEVICE_CATEGORY_EARPIECE", { {0, -2700}, {33, -1800}, {66, -900}, {100, 0} } },
+ {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } },
+ {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } },
+ },
+ },
+ {"system", "AUDIO_STREAM_SYSTEM", 0, 100,
+ {
+ {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } },
+ {"DEVICE_CATEGORY_SPEAKER", { {1, -5100}, {57, -2800}, {71, -2500}, {85, -2300}, {100, -2100} } },
+ {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } },
+ {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE
+ {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE
+ },
+ },
+ {"ring", "AUDIO_STREAM_RING", 0, 100,
+ {
+ {"DEVICE_CATEGORY_HEADSET", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE
+ {"DEVICE_CATEGORY_SPEAKER", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } },
+ {"DEVICE_CATEGORY_EARPIECE", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_EARPIECE_VOLUME_CURVE
+ {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE
+ {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE
+ },
+ },
+ {"music", "AUDIO_STREAM_MUSIC", 0, 40,
+ {
+ {"DEVICE_CATEGORY_HEADSET", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE
+ {"DEVICE_CATEGORY_SPEAKER", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_SPEAKER_VOLUME_CURVE
+ {"DEVICE_CATEGORY_EARPIECE", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE
+ {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE
+ {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE
+ },
+ },
+ {"alarm", "AUDIO_STREAM_ALARM", 0, 100,
+ {
+ {"DEVICE_CATEGORY_HEADSET", { {0, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEADSET_VOLUME_CURVE
+ {"DEVICE_CATEGORY_SPEAKER", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_SPEAKER_VOLUME_CURVE
+ {"DEVICE_CATEGORY_EARPIECE", { {0, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_EARPIECE_VOLUME_CURVE
+ {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_NON_MUTABLE_EXT_VOLUME_CURVE
+ {"DEVICE_CATEGORY_HEARING_AID", { {0, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE
+ },
+ },
+ {"notification", "AUDIO_STREAM_NOTIFICATION", 0, 100,
+ {
+ {"DEVICE_CATEGORY_HEADSET", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE
+ {"DEVICE_CATEGORY_SPEAKER", { {1, -4680}, {42, -2070}, {85, -540}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_SPEAKER_SYSTEM_VOLUME_CURVE
+ {"DEVICE_CATEGORY_EARPIECE", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_EARPIECE_VOLUME_CURVE
+ {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE
+ {"DEVICE_CATEGORY_HEARING_AID", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE
+ },
+ },
+ {"bluetooth_sco", "AUDIO_STREAM_BLUETOOTH_SCO", 1, 10,
+ {
+ {"DEVICE_CATEGORY_HEADSET", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } },
+ {"DEVICE_CATEGORY_SPEAKER", { {0, -2400}, {33, -1600}, {66, -800}, {100, 0} } },
+ {"DEVICE_CATEGORY_EARPIECE", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } },
+ {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE
+ {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE
+ },
+ },
+ {"enforced_audible", "AUDIO_STREAM_ENFORCED_AUDIBLE", 0, 100,
+ {
+ {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } },
+ {"DEVICE_CATEGORY_SPEAKER", { {1, -3400}, {71, -2400}, {100, -2000} } },
+ {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, // DEFAULT_SYSTEM_VOLUME_CURVE
+ {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE
+ {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE
+ },
+ },
+ {"dtmf", "AUDIO_STREAM_DTMF", 0, 100,
+ {
+ {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } },
+ {"DEVICE_CATEGORY_SPEAKER", { {1, -4000}, {71, -2400}, {100, -1400} } }, // DEFAULT_SYSTEM_VOLUME_CURVE
+ {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, // DEFAULT_SYSTEM_VOLUME_CURVE
+ {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE
+ {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE
+ },
+ },
+ {"tts", "AUDIO_STREAM_TTS", 0, 16,
+ {
+ {"DEVICE_CATEGORY_HEADSET", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE
+ {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+ {"DEVICE_CATEGORY_EARPIECE", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE
+ {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE
+ {"DEVICE_CATEGORY_HEARING_AID", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE
+ },
+ },
+ {"accessibility", "AUDIO_STREAM_ACCESSIBILITY", 1, 40,
+ {
+ {"DEVICE_CATEGORY_HEADSET", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE
+ {"DEVICE_CATEGORY_SPEAKER", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_SPEAKER_VOLUME_CURVE
+ {"DEVICE_CATEGORY_EARPIECE", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE
+ {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE
+ {"DEVICE_CATEGORY_HEARING_AID", { {0, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE
+ },
+ },
+ {"rerouting", "AUDIO_STREAM_REROUTING", 0, 1,
+ {
+ {"DEVICE_CATEGORY_HEADSET", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+ {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+ {"DEVICE_CATEGORY_EARPIECE", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+ {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+ {"DEVICE_CATEGORY_HEARING_AID", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+ },
+ },
+ {"patch", "AUDIO_STREAM_PATCH", 0, 1,
+ {
+ {"DEVICE_CATEGORY_HEADSET", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+ {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+ {"DEVICE_CATEGORY_EARPIECE", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+ {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+ {"DEVICE_CATEGORY_HEARING_AID", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+ },
+ },
+};
+
const engineConfig::Config gDefaultEngineConfig = {
1.0,
gOrderedStrategies,
{},
- {}
+ {},
+ gVolumeGroups
};
} // namespace android
diff --git a/services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp b/services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp
new file mode 100644
index 0000000..fe3b000
--- /dev/null
+++ b/services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "APM::Engine::StreamVolumeCurves"
+//#define LOG_NDEBUG 0
+
+#include "StreamVolumeCurves.h"
+#include <TypeConverter.h>
+
+namespace android {
+
+void StreamVolumeCurves::dump(String8 *dst, int spaces) const
+{
+ if (mCurves.empty()) {
+ return;
+ }
+ dst->appendFormat("\n%*sStreams dump:\n", spaces, "");
+ dst->appendFormat(
+ "%*sStream Can be muted Index Min Index Max Index Cur [device : index]...\n", spaces + 2, "");
+ for (const auto &streamCurve : mCurves) {
+ streamCurve.second.dump(dst, spaces + 2, false);
+ }
+ dst->appendFormat("\n%*sVolume Curves for Use Cases (aka Stream types) dump:\n", spaces, "");
+ for (const auto &streamCurve : mCurves) {
+ std::string streamTypeLiteral;
+ StreamTypeConverter::toString(streamCurve.first, streamTypeLiteral);
+ dst->appendFormat(
+ " %s (%02d): Curve points for device category (index, attenuation in millibel)\n",
+ streamTypeLiteral.c_str(), streamCurve.first);
+ streamCurve.second.dump(dst, spaces + 2, true);
+ }
+}
+
+} // namespace android
diff --git a/services/audiopolicy/engine/common/src/VolumeCurve.cpp b/services/audiopolicy/engine/common/src/VolumeCurve.cpp
new file mode 100644
index 0000000..be2ca73
--- /dev/null
+++ b/services/audiopolicy/engine/common/src/VolumeCurve.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "APM::VolumeCurve"
+//#define LOG_NDEBUG 0
+
+#include "VolumeCurve.h"
+#include "TypeConverter.h"
+
+namespace android {
+
+float VolumeCurve::volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) const
+{
+ ALOG_ASSERT(!mCurvePoints.isEmpty(), "Invalid volume curve");
+
+ if (indexInUi < volIndexMin) {
+ // an index of 0 means mute request when volIndexMin > 0
+ if (indexInUi == 0) {
+ ALOGV("VOLUME forcing mute for index 0 with min index %d", volIndexMin);
+ return VOLUME_MIN_DB;
+ }
+ ALOGV("VOLUME remapping index from %d to min index %d", indexInUi, volIndexMin);
+ indexInUi = volIndexMin;
+ } else if (indexInUi > volIndexMax) {
+ ALOGV("VOLUME remapping index from %d to max index %d", indexInUi, volIndexMax);
+ indexInUi = volIndexMax;
+ }
+
+ size_t nbCurvePoints = mCurvePoints.size();
+ // the volume index in the UI is relative to the min and max volume indices for this stream
+ int nbSteps = 1 + mCurvePoints[nbCurvePoints - 1].mIndex - mCurvePoints[0].mIndex;
+ int volIdx = (nbSteps * (indexInUi - volIndexMin)) / (volIndexMax - volIndexMin);
+
+ // Where would this volume index been inserted in the curve point
+ size_t indexInUiPosition = mCurvePoints.orderOf(CurvePoint(volIdx, 0));
+ if (indexInUiPosition >= nbCurvePoints) {
+ //use last point of table
+ return mCurvePoints[nbCurvePoints - 1].mAttenuationInMb / 100.0f;
+ }
+ if (indexInUiPosition == 0) {
+ if (indexInUiPosition != mCurvePoints[0].mIndex) {
+ return VOLUME_MIN_DB; // out of bounds
+ }
+ return mCurvePoints[0].mAttenuationInMb / 100.0f;
+ }
+ // linear interpolation in the attenuation table in dB
+ float decibels = (mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f) +
+ ((float)(volIdx - mCurvePoints[indexInUiPosition - 1].mIndex)) *
+ ( ((mCurvePoints[indexInUiPosition].mAttenuationInMb / 100.0f) -
+ (mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f)) /
+ ((float)(mCurvePoints[indexInUiPosition].mIndex -
+ mCurvePoints[indexInUiPosition - 1].mIndex)) );
+
+ ALOGV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f]",
+ mCurvePoints[indexInUiPosition - 1].mIndex, volIdx,
+ mCurvePoints[indexInUiPosition].mIndex,
+ ((float)mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f), decibels,
+ ((float)mCurvePoints[indexInUiPosition].mAttenuationInMb / 100.0f));
+
+ return decibels;
+}
+
+void VolumeCurve::dump(String8 *dst, int spaces, bool curvePoints) const
+{
+ if (!curvePoints) {
+ return;
+ }
+ dst->append(" {");
+ for (size_t i = 0; i < mCurvePoints.size(); i++) {
+ dst->appendFormat("%*s (%3d, %5d)", spaces, "",
+ mCurvePoints[i].mIndex, mCurvePoints[i].mAttenuationInMb);
+ dst->appendFormat(i == (mCurvePoints.size() - 1) ? " }\n" : ", ");
+ }
+}
+
+void VolumeCurves::dump(String8 *dst, int spaces, bool curvePoints) const
+{
+ if (!curvePoints) {
+ dst->appendFormat("%*s%02d %s %03d %03d ", spaces, "",
+ mStream, mCanBeMuted ? "true " : "false", mIndexMin, mIndexMax);
+ for (const auto &pair : mIndexCur) {
+ dst->appendFormat("%*s %04x : %02d, ", spaces, "", pair.first, pair.second);
+ }
+ dst->appendFormat("\n");
+ return;
+ }
+ for (size_t i = 0; i < size(); i++) {
+ std::string deviceCatLiteral;
+ DeviceCategoryConverter::toString(keyAt(i), deviceCatLiteral);
+ dst->appendFormat("%*s %s :", spaces, "", deviceCatLiteral.c_str());
+ valueAt(i)->dump(dst, 2, true);
+ }
+}
+
+} // namespace android
diff --git a/services/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h
index e18f687..f7caad2 100644
--- a/services/audiopolicy/engine/config/include/EngineConfig.h
+++ b/services/audiopolicy/engine/config/include/EngineConfig.h
@@ -45,6 +45,27 @@
using AttributesGroups = std::vector<AttributesGroup>;
+struct CurvePoint {
+ int index;
+ int attenuationInMb;
+};
+using CurvePoints = std::vector<CurvePoint>;
+
+struct VolumeCurve {
+ std::string deviceCategory;
+ CurvePoints curvePoints;
+};
+using VolumeCurves = std::vector<VolumeCurve>;
+
+struct VolumeGroup {
+ std::string name;
+ std::string stream;
+ int indexMin;
+ int indexMax;
+ VolumeCurves volumeCurves;
+};
+using VolumeGroups = std::vector<VolumeGroup>;
+
struct ProductStrategy {
std::string name;
AttributesGroups attributesGroups;
@@ -78,6 +99,7 @@
ProductStrategies productStrategies;
Criteria criteria;
CriterionTypes criterionTypes;
+ VolumeGroups volumeGroups;
};
/** Result of `parse(const char*)` */
diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp
index 3aa38cf..227ebd8 100644
--- a/services/audiopolicy/engine/config/src/EngineConfig.cpp
+++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp
@@ -107,6 +107,35 @@
static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root,
Collection &collection);
};
+struct VolumeTraits : public BaseSerializerTraits<VolumeCurve, VolumeCurves> {
+ static constexpr const char *tag = "volume";
+ static constexpr const char *collectionTag = "volumes";
+ static constexpr const char *volumePointTag = "point";
+
+ struct Attributes {
+ static constexpr const char *deviceCategory = "deviceCategory";
+ static constexpr const char *reference = "ref"; /**< For volume curves factorization. */
+ };
+
+ static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root,
+ Collection &collection);
+};
+struct VolumeGroupTraits : public BaseSerializerTraits<VolumeGroup, VolumeGroups> {
+ static constexpr const char *tag = "volumeGroup";
+ static constexpr const char *collectionTag = "volumeGroups";
+
+ struct Attributes {
+ static constexpr const char *name = "name";
+ static constexpr const char *stream = "stream"; // For legacy volume curves
+ static constexpr const char *indexMin = "indexMin";
+ static constexpr const char *indexMax = "indexMax";
+ };
+
+ static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root,
+ Collection &collection);
+};
+
+using xmlCharUnique = std::unique_ptr<xmlChar, decltype(xmlFree)>;
using xmlCharUnique = std::unique_ptr<xmlChar, decltype(xmlFree)>;
@@ -383,6 +412,100 @@
return NO_ERROR;
}
+status_t VolumeTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &volumes)
+{
+ std::string deviceCategory = getXmlAttribute(root, Attributes::deviceCategory);
+ if (deviceCategory.empty()) {
+ ALOGW("%s: No %s found", __FUNCTION__, Attributes::deviceCategory);
+ }
+
+ std::string referenceName = getXmlAttribute(root, Attributes::reference);
+ const _xmlNode *ref = NULL;
+ if (!referenceName.empty()) {
+ getReference(xmlDocGetRootElement(doc), ref, referenceName, collectionTag);
+ if (ref == NULL) {
+ ALOGE("%s: No reference Ptr found for %s", __FUNCTION__, referenceName.c_str());
+ return BAD_VALUE;
+ }
+ }
+ // Retrieve curve point from reference element if found or directly from current curve
+ CurvePoints curvePoints;
+ for (const xmlNode *child = referenceName.empty() ?
+ root->xmlChildrenNode : ref->xmlChildrenNode; child != NULL; child = child->next) {
+ if (!xmlStrcmp(child->name, (const xmlChar *)volumePointTag)) {
+ xmlCharUnique pointXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree);
+ if (pointXml == NULL) {
+ return BAD_VALUE;
+ }
+ ALOGV("%s: %s=%s", __func__, tag, reinterpret_cast<const char*>(pointXml.get()));
+ std::vector<int> point;
+ collectionFromString<DefaultTraits<int>>(
+ reinterpret_cast<const char*>(pointXml.get()), point, ",");
+ if (point.size() != 2) {
+ ALOGE("%s: Invalid %s: %s", __func__, volumePointTag,
+ reinterpret_cast<const char*>(pointXml.get()));
+ return BAD_VALUE;
+ }
+ curvePoints.push_back({point[0], point[1]});
+ }
+ }
+ volumes.push_back({ deviceCategory, curvePoints });
+ return NO_ERROR;
+}
+
+status_t VolumeGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &volumes)
+{
+ std::string name;
+ std::string stream = {};
+ int indexMin = 0;
+ int indexMax = 0;
+
+ for (const xmlNode *child = root->xmlChildrenNode; child != NULL; child = child->next) {
+ if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::name)) {
+ xmlCharUnique nameXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree);
+ if (nameXml == nullptr) {
+ return BAD_VALUE;
+ }
+ name = reinterpret_cast<const char*>(nameXml.get());
+ }
+ if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::stream)) {
+ xmlCharUnique streamXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree);
+ if (streamXml == nullptr) {
+ return BAD_VALUE;
+ }
+ stream = reinterpret_cast<const char*>(streamXml.get());
+ }
+ if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::indexMin)) {
+ xmlCharUnique indexMinXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree);
+ if (indexMinXml == nullptr) {
+ return BAD_VALUE;
+ }
+ std::string indexMinLiteral(reinterpret_cast<const char*>(indexMinXml.get()));
+ if (!convertTo(indexMinLiteral, indexMin)) {
+ return BAD_VALUE;
+ }
+ }
+ if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::indexMax)) {
+ xmlCharUnique indexMaxXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree);
+ if (indexMaxXml == nullptr) {
+ return BAD_VALUE;
+ }
+ std::string indexMaxLiteral(reinterpret_cast<const char*>(indexMaxXml.get()));
+ if (!convertTo(indexMaxLiteral, indexMax)) {
+ return BAD_VALUE;
+ }
+ }
+ }
+ ALOGV("%s: group=%s stream=%s indexMin=%d, indexMax=%d",
+ __func__, name.c_str(), stream.c_str(), indexMin, indexMax);
+
+ VolumeCurves groupVolumeCurves;
+ size_t skipped = 0;
+ deserializeCollection<VolumeTraits>(doc, root, groupVolumeCurves, skipped);
+ volumes.push_back({ name, stream, indexMin, indexMax, groupVolumeCurves });
+ return NO_ERROR;
+}
+
ParsingResult parse(const char* path) {
xmlDocPtr doc;
doc = xmlParseFile(path);
@@ -414,6 +537,9 @@
doc, cur, config->criteria, nbSkippedElements);
deserializeCollection<CriterionTypeTraits>(
doc, cur, config->criterionTypes, nbSkippedElements);
+ deserializeCollection<VolumeGroupTraits>(
+ doc, cur, config->volumeGroups, nbSkippedElements);
+
return {std::move(config), nbSkippedElements};
}
diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
index 498cc3b..c9e9507 100644
--- a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
+++ b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
@@ -18,6 +18,7 @@
#include <AudioPolicyManagerObserver.h>
#include <media/AudioProductStrategy.h>
+#include <IVolumeCurves.h>
#include <policy.h>
#include <Volume.h>
#include <HwModule.h>
@@ -234,6 +235,21 @@
*/
virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) const = 0;
+ /**
+ * @brief getVolumeCurvesForAttributes retrieves the Volume Curves interface for the
+ * requested Audio Attributes.
+ * @param attr to be considered
+ * @return IVolumeCurves interface pointer if found, nullptr otherwise
+ */
+ virtual IVolumeCurves *getVolumeCurvesForAttributes(const audio_attributes_t &attr) = 0;
+
+ /**
+ * @brief getVolumeCurvesForStreamType retrieves the Volume Curves interface for the stream
+ * @param stream to be considered
+ * @return IVolumeCurves interface pointer if found, nullptr otherwise
+ */
+ virtual IVolumeCurves *getVolumeCurvesForStreamType(audio_stream_type_t stream) = 0;
+
virtual void dump(String8 *dst) const = 0;
protected:
diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h b/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h
index b7902cf..43ba625 100644
--- a/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h
+++ b/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h
@@ -16,7 +16,6 @@
#pragma once
-#include <IVolumeCurvesCollection.h>
#include <AudioGain.h>
#include <AudioPort.h>
#include <AudioPatch.h>
@@ -51,8 +50,6 @@
virtual const DeviceVector &getAvailableInputDevices() const = 0;
- virtual IVolumeCurvesCollection &getVolumeCurves() = 0;
-
virtual const sp<DeviceDescriptor> &getDefaultOutputDevice() const = 0;
protected: