1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include "IVolumeCurvesCollection.h"
20 #include <policy.h>
21 #include <utils/RefBase.h>
22 #include <utils/String8.h>
23 #include <utils/SortedVector.h>
24 #include <utils/KeyedVector.h>
25 #include <system/audio.h>
26 #include <cutils/config_utils.h>
27 #include <string>
28 #include <utility>
29 
30 namespace android {
31 
32 struct CurvePoint
33 {
CurvePointCurvePoint34     CurvePoint() {}
CurvePointCurvePoint35     CurvePoint(int index, int attenuationInMb) :
36         mIndex(index), mAttenuationInMb(attenuationInMb) {}
37     uint32_t mIndex;
38     int mAttenuationInMb;
39 };
40 
41 inline bool operator< (const CurvePoint &lhs, const CurvePoint &rhs)
42 {
43     return lhs.mIndex < rhs.mIndex;
44 }
45 
46 // A volume curve for a given use case and device category
47 // It contains of list of points of this curve expressing the attenuation in Millibels for
48 // a given volume index from 0 to 100
49 class VolumeCurve : public RefBase
50 {
51 public:
VolumeCurve(device_category device,audio_stream_type_t stream)52     VolumeCurve(device_category device, audio_stream_type_t stream) :
53         mDeviceCategory(device), mStreamType(stream) {}
54 
getDeviceCategory()55     device_category getDeviceCategory() const { return mDeviceCategory; }
getStreamType()56     audio_stream_type_t getStreamType() const { return mStreamType; }
57 
add(const CurvePoint & point)58     void add(const CurvePoint &point) { mCurvePoints.add(point); }
59 
60     float volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) const;
61 
62     void dump(int fd) const;
63 
64 private:
65     SortedVector<CurvePoint> mCurvePoints;
66     device_category mDeviceCategory;
67     audio_stream_type_t mStreamType;
68 };
69 
70 // Volume Curves for a given use case indexed by device category
71 class VolumeCurvesForStream : public KeyedVector<device_category, sp<VolumeCurve> >
72 {
73 public:
VolumeCurvesForStream()74     VolumeCurvesForStream() : mIndexMin(0), mIndexMax(1), mCanBeMuted(true)
75     {
76         mIndexCur.add(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, 0);
77     }
78 
getCurvesFor(device_category device)79     sp<VolumeCurve> getCurvesFor(device_category device) const
80     {
81         if (indexOfKey(device) < 0) {
82             return 0;
83         }
84         return valueFor(device);
85     }
86 
getVolumeIndex(audio_devices_t device)87     int getVolumeIndex(audio_devices_t device) const
88     {
89         device = Volume::getDeviceForVolume(device);
90         // there is always a valid entry for AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME
91         if (mIndexCur.indexOfKey(device) < 0) {
92             device = AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME;
93         }
94         return mIndexCur.valueFor(device);
95     }
96 
canBeMuted()97     bool canBeMuted() const { return mCanBeMuted; }
clearCurrentVolumeIndex()98     void clearCurrentVolumeIndex() { mIndexCur.clear(); }
addCurrentVolumeIndex(audio_devices_t device,int index)99     void addCurrentVolumeIndex(audio_devices_t device, int index) { mIndexCur.add(device, index); }
100 
setVolumeIndexMin(int volIndexMin)101     void setVolumeIndexMin(int volIndexMin) { mIndexMin = volIndexMin; }
getVolumeIndexMin()102     int getVolumeIndexMin() const { return mIndexMin; }
103 
setVolumeIndexMax(int volIndexMax)104     void setVolumeIndexMax(int volIndexMax) { mIndexMax = volIndexMax; }
getVolumeIndexMax()105     int getVolumeIndexMax() const { return mIndexMax; }
106 
hasVolumeIndexForDevice(audio_devices_t device)107     bool hasVolumeIndexForDevice(audio_devices_t device) const
108     {
109         device = Volume::getDeviceForVolume(device);
110         return mIndexCur.indexOfKey(device) >= 0;
111     }
112 
getOriginVolumeCurve(device_category deviceCategory)113     const sp<VolumeCurve> getOriginVolumeCurve(device_category deviceCategory) const
114     {
115         ALOG_ASSERT(mOriginVolumeCurves.indexOfKey(deviceCategory) >= 0, "Invalid device category");
116         return mOriginVolumeCurves.valueFor(deviceCategory);
117     }
setVolumeCurve(device_category deviceCategory,const sp<VolumeCurve> & volumeCurve)118     void setVolumeCurve(device_category deviceCategory, const sp<VolumeCurve> &volumeCurve)
119     {
120         ALOG_ASSERT(indexOfKey(deviceCategory) >= 0, "Invalid device category for Volume Curve");
121         replaceValueFor(deviceCategory, volumeCurve);
122     }
123 
add(const sp<VolumeCurve> & volumeCurve)124     ssize_t add(const sp<VolumeCurve> &volumeCurve)
125     {
126         device_category deviceCategory = volumeCurve->getDeviceCategory();
127         ssize_t index = indexOfKey(deviceCategory);
128         if (index < 0) {
129             // Keep track of original Volume Curves per device category in order to switch curves.
130             mOriginVolumeCurves.add(deviceCategory, volumeCurve);
131             return KeyedVector::add(deviceCategory, volumeCurve);
132         }
133         return index;
134     }
135 
volIndexToDb(device_category deviceCat,int indexInUi)136     float volIndexToDb(device_category deviceCat, int indexInUi) const
137     {
138         sp<VolumeCurve> vc = getCurvesFor(deviceCat);
139         if (vc != 0) {
140             return vc->volIndexToDb(indexInUi, mIndexMin, mIndexMax);
141         } else {
142             ALOGE("Invalid device category %d for Volume Curve", deviceCat);
143             return 0.0f;
144         }
145     }
146 
147     void dump(int fd, int spaces, bool curvePoints = false) const;
148 
149 private:
150     KeyedVector<device_category, sp<VolumeCurve> > mOriginVolumeCurves;
151     KeyedVector<audio_devices_t, int> mIndexCur; /**< current volume index per device. */
152     int mIndexMin; /**< min volume index. */
153     int mIndexMax; /**< max volume index. */
154     bool mCanBeMuted; /**< true is the stream can be muted. */
155 };
156 
157 // Collection of Volume Curves indexed by use case
158 class VolumeCurvesCollection : public KeyedVector<audio_stream_type_t, VolumeCurvesForStream>,
159                                public IVolumeCurvesCollection
160 {
161 public:
VolumeCurvesCollection()162     VolumeCurvesCollection()
163     {
164         // Create an empty collection of curves
165         for (ssize_t i = 0 ; i < AUDIO_STREAM_CNT; i++) {
166             audio_stream_type_t stream = static_cast<audio_stream_type_t>(i);
167             KeyedVector::add(stream, VolumeCurvesForStream());
168         }
169     }
170 
171     // Once XML has been parsed, must be call first to sanity check table and initialize indexes
initStreamVolume(audio_stream_type_t stream,int indexMin,int indexMax)172     virtual status_t initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax)
173     {
174         editValueAt(stream).setVolumeIndexMin(indexMin);
175         editValueAt(stream).setVolumeIndexMax(indexMax);
176         return NO_ERROR;
177     }
clearCurrentVolumeIndex(audio_stream_type_t stream)178     virtual void clearCurrentVolumeIndex(audio_stream_type_t stream)
179     {
180         editCurvesFor(stream).clearCurrentVolumeIndex();
181     }
addCurrentVolumeIndex(audio_stream_type_t stream,audio_devices_t device,int index)182     virtual void addCurrentVolumeIndex(audio_stream_type_t stream, audio_devices_t device, int index)
183     {
184         editCurvesFor(stream).addCurrentVolumeIndex(device, index);
185     }
canBeMuted(audio_stream_type_t stream)186     virtual bool canBeMuted(audio_stream_type_t stream) { return getCurvesFor(stream).canBeMuted(); }
187 
getVolumeIndexMin(audio_stream_type_t stream)188     virtual int getVolumeIndexMin(audio_stream_type_t stream) const
189     {
190         return getCurvesFor(stream).getVolumeIndexMin();
191     }
getVolumeIndexMax(audio_stream_type_t stream)192     virtual int getVolumeIndexMax(audio_stream_type_t stream) const
193     {
194         return getCurvesFor(stream).getVolumeIndexMax();
195     }
getVolumeIndex(audio_stream_type_t stream,audio_devices_t device)196     virtual int getVolumeIndex(audio_stream_type_t stream, audio_devices_t device)
197     {
198         return getCurvesFor(stream).getVolumeIndex(device);
199     }
switchVolumeCurve(audio_stream_type_t streamSrc,audio_stream_type_t streamDst)200     virtual void switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst)
201     {
202         const VolumeCurvesForStream &sourceCurves = getCurvesFor(streamSrc);
203         VolumeCurvesForStream &dstCurves = editCurvesFor(streamDst);
204         ALOG_ASSERT(sourceCurves.size() == dstCurves.size(), "device category not aligned");
205         for (size_t index = 0; index < sourceCurves.size(); index++) {
206             device_category cat = sourceCurves.keyAt(index);
207             dstCurves.setVolumeCurve(cat, sourceCurves.getOriginVolumeCurve(cat));
208         }
209     }
volIndexToDb(audio_stream_type_t stream,device_category cat,int indexInUi)210     virtual float volIndexToDb(audio_stream_type_t stream, device_category cat, int indexInUi) const
211     {
212         return getCurvesFor(stream).volIndexToDb(cat, indexInUi);
213     }
hasVolumeIndexForDevice(audio_stream_type_t stream,audio_devices_t device)214     virtual bool hasVolumeIndexForDevice(audio_stream_type_t stream,
215                                          audio_devices_t device) const
216     {
217         return getCurvesFor(stream).hasVolumeIndexForDevice(device);
218     }
219 
220     virtual status_t dump(int fd) const;
221 
add(const sp<VolumeCurve> & volumeCurve)222     ssize_t add(const sp<VolumeCurve> &volumeCurve)
223     {
224         audio_stream_type_t streamType = volumeCurve->getStreamType();
225         return editCurvesFor(streamType).add(volumeCurve);
226     }
editCurvesFor(audio_stream_type_t stream)227     VolumeCurvesForStream &editCurvesFor(audio_stream_type_t stream)
228     {
229         ALOG_ASSERT(indexOfKey(stream) >= 0, "Invalid stream type for Volume Curve");
230         return editValueAt(stream);
231     }
getCurvesFor(audio_stream_type_t stream)232     const VolumeCurvesForStream &getCurvesFor(audio_stream_type_t stream) const
233     {
234         ALOG_ASSERT(indexOfKey(stream) >= 0, "Invalid stream type for Volume Curve");
235         return valueFor(stream);
236     }
237 };
238 
239 } // namespace android
240