1/*
2 * Copyright (C) 2010 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/* EffectSend implementation */
18
19#include "sles_allinclusive.h"
20
21
22/** \brief Maps AUX index to OutputMix interface index */
23
24static const unsigned char AUX_to_MPH[AUX_MAX] = {
25    MPH_ENVIRONMENTALREVERB,
26    MPH_PRESETREVERB
27};
28
29
30/** \brief This is a private function that validates the effect interface specified by the
31 *  application when it calls EnableEffectSend, IsEnabled, SetSendLevel, or GetSendLevel.
32 *  For the interface to be valid, it has to satisfy these requirements:
33 *   - object is an audio player (MIDI player is not supported yet)
34 *   - audio sink is an output mix
35 *   - interface was exposed at object creation time or by DynamicInterface::AddInterface
36 *   - interface was "gotten" with Object::GetInterface
37 */
38
39static struct EnableLevel *getEnableLevel(IEffectSend *this, const void *pAuxEffect)
40{
41    // Make sure this effect send is on an audio player, not a MIDI player
42    CAudioPlayer *audioPlayer = (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(this)) ?
43        (CAudioPlayer *) this->mThis : NULL;
44    if (NULL == audioPlayer) {
45        return NULL;
46    }
47    // Get the output mix for this player
48    COutputMix *outputMix = CAudioPlayer_GetOutputMix(audioPlayer);
49    unsigned aux;
50    if (pAuxEffect == &outputMix->mEnvironmentalReverb.mItf) {
51        aux = AUX_ENVIRONMENTALREVERB;
52    } else if (pAuxEffect == &outputMix->mPresetReverb.mItf) {
53        aux = AUX_PRESETREVERB;
54    } else {
55        SL_LOGE("EffectSend on unknown aux effect %p", pAuxEffect);
56        return NULL;
57    }
58    assert(aux < AUX_MAX);
59    // Validate that the application has a valid interface for the effect.  The interface must have
60    // been exposed at object creation time or by DynamicInterface::AddInterface, and it also must
61    // have been "gotten" with Object::GetInterface.
62    unsigned MPH = AUX_to_MPH[aux];
63    int index = MPH_to_OutputMix[MPH];
64    if (0 > index) {
65        SL_LOGE("EffectSend aux=%u MPH=%u", aux, MPH);
66        return NULL;
67    }
68    unsigned mask = 1 << index;
69    object_lock_shared(&outputMix->mObject);
70    SLuint32 state = outputMix->mObject.mInterfaceStates[index];
71    mask &= outputMix->mObject.mGottenMask;
72    object_unlock_shared(&outputMix->mObject);
73    switch (state) {
74    case INTERFACE_EXPOSED:
75    case INTERFACE_ADDED:
76    case INTERFACE_SUSPENDED:
77    case INTERFACE_SUSPENDING:
78    case INTERFACE_RESUMING_1:
79    case INTERFACE_RESUMING_2:
80        if (mask) {
81            return &this->mEnableLevels[aux];
82        }
83        SL_LOGE("EffectSend no GetInterface yet");
84        break;
85    default:
86        SL_LOGE("EffectSend invalid interface state %lu", state);
87        break;
88    }
89    return NULL;
90}
91
92#if defined(ANDROID) && !defined(USE_BACKPORT)
93/** \brief This is a private function that translates an Android effect framework status code
94 *  to the SL ES result code used in the EnableEffectSend() function of the SLEffectSendItf
95 *  interface.
96 */
97static SLresult translateEnableFxSendError(android::status_t status) {
98    switch (status) {
99    case android::NO_ERROR:
100        return SL_RESULT_SUCCESS;
101    case android::INVALID_OPERATION:
102    case android::BAD_VALUE:
103    default:
104        SL_LOGE("EffectSend status %u", status);
105        return SL_RESULT_RESOURCE_ERROR;
106    }
107}
108#endif
109
110
111static SLresult IEffectSend_EnableEffectSend(SLEffectSendItf self,
112    const void *pAuxEffect, SLboolean enable, SLmillibel initialLevel)
113{
114    SL_ENTER_INTERFACE
115
116    if (!((SL_MILLIBEL_MIN <= initialLevel) && (initialLevel <= 0))) {
117        result = SL_RESULT_PARAMETER_INVALID;
118    } else {
119        IEffectSend *this = (IEffectSend *) self;
120        struct EnableLevel *enableLevel = getEnableLevel(this, pAuxEffect);
121        if (NULL == enableLevel) {
122            result = SL_RESULT_PARAMETER_INVALID;
123        } else {
124            interface_lock_exclusive(this);
125            enableLevel->mEnable = SL_BOOLEAN_FALSE != enable; // normalize
126            enableLevel->mSendLevel = initialLevel;
127#if !defined(ANDROID) || defined(USE_BACKPORT)
128            result = SL_RESULT_SUCCESS;
129#else
130            // TODO do not repeat querying of CAudioPlayer, done inside getEnableLevel()
131            CAudioPlayer *ap = (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(this)) ?
132                    (CAudioPlayer *) this->mThis : NULL;
133            // note that if this was a MIDI player, getEnableLevel would have returned NULL
134            assert(NULL != ap);
135            // check which effect the send is attached to, attach and set level
136            COutputMix *outputMix = CAudioPlayer_GetOutputMix(ap);
137            // the initial send level set here is the total energy on the aux bus,
138            //  so it must take into account the player volume level
139            if (pAuxEffect == &outputMix->mPresetReverb.mItf) {
140                result = translateEnableFxSendError(android_fxSend_attach(ap, (bool) enable,
141                    outputMix->mPresetReverb.mPresetReverbEffect,
142                    initialLevel + ap->mVolume.mLevel));
143            } else if (pAuxEffect == &outputMix->mEnvironmentalReverb.mItf) {
144                result = translateEnableFxSendError(android_fxSend_attach(ap, (bool) enable,
145                    outputMix->mEnvironmentalReverb.mEnvironmentalReverbEffect,
146                    initialLevel + ap->mVolume.mLevel));
147            } else {
148                SL_LOGE("EffectSend unknown aux effect %p", pAuxEffect);
149                result = SL_RESULT_PARAMETER_INVALID;
150            }
151#endif
152            interface_unlock_exclusive(this);
153        }
154    }
155
156    SL_LEAVE_INTERFACE
157}
158
159
160static SLresult IEffectSend_IsEnabled(SLEffectSendItf self,
161    const void *pAuxEffect, SLboolean *pEnable)
162{
163    SL_ENTER_INTERFACE
164
165    if (NULL == pEnable) {
166        result = SL_RESULT_PARAMETER_INVALID;
167    } else {
168        IEffectSend *this = (IEffectSend *) self;
169        struct EnableLevel *enableLevel = getEnableLevel(this, pAuxEffect);
170        if (NULL == enableLevel) {
171            *pEnable = SL_BOOLEAN_FALSE;
172            result = SL_RESULT_PARAMETER_INVALID;
173        } else {
174            interface_lock_shared(this);
175            SLboolean enable = enableLevel->mEnable;
176            interface_unlock_shared(this);
177            *pEnable = enable;
178            result = SL_RESULT_SUCCESS;
179        }
180    }
181
182    SL_LEAVE_INTERFACE
183}
184
185
186static SLresult IEffectSend_SetDirectLevel(SLEffectSendItf self, SLmillibel directLevel)
187{
188    SL_ENTER_INTERFACE
189
190    if (!((SL_MILLIBEL_MIN <= directLevel) && (directLevel <= 0))) {
191        result = SL_RESULT_PARAMETER_INVALID;
192    } else {
193        IEffectSend *this = (IEffectSend *) self;
194        interface_lock_exclusive(this);
195        CAudioPlayer *ap = (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(this)) ?
196                (CAudioPlayer *) this->mThis : NULL;
197        if (NULL != ap) {
198            SLmillibel oldDirectLevel = ap->mDirectLevel;
199            if (oldDirectLevel != directLevel) {
200                ap->mDirectLevel = directLevel;
201#if defined(ANDROID)
202                ap->mAmplFromDirectLevel = sles_to_android_amplification(directLevel);
203                interface_unlock_exclusive_attributes(this, ATTR_GAIN);
204#else
205                interface_unlock_exclusive(this);
206#endif
207            } else {
208                interface_unlock_exclusive(this);
209            }
210        } else {
211            // MIDI player is silently not supported
212            interface_unlock_exclusive(this);
213        }
214        result = SL_RESULT_SUCCESS;
215    }
216
217    SL_LEAVE_INTERFACE
218}
219
220
221static SLresult IEffectSend_GetDirectLevel(SLEffectSendItf self, SLmillibel *pDirectLevel)
222{
223    SL_ENTER_INTERFACE
224
225    if (NULL == pDirectLevel) {
226        result = SL_RESULT_PARAMETER_INVALID;
227    } else {
228        IEffectSend *this = (IEffectSend *) self;
229        interface_lock_shared(this);
230        CAudioPlayer *ap = (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(this)) ?
231                (CAudioPlayer *) this->mThis : NULL;
232        if (NULL != ap) {
233            *pDirectLevel = ap->mDirectLevel;
234        } else {
235            // MIDI player is silently not supported
236            *pDirectLevel = 0;
237        }
238        interface_unlock_shared(this);
239        result = SL_RESULT_SUCCESS;
240    }
241
242    SL_LEAVE_INTERFACE
243}
244
245
246static SLresult IEffectSend_SetSendLevel(SLEffectSendItf self, const void *pAuxEffect,
247    SLmillibel sendLevel)
248{
249    SL_ENTER_INTERFACE
250
251    if (!((SL_MILLIBEL_MIN <= sendLevel) && (sendLevel <= 0))) {
252        result = SL_RESULT_PARAMETER_INVALID;
253    } else {
254        IEffectSend *this = (IEffectSend *) self;
255        struct EnableLevel *enableLevel = getEnableLevel(this, pAuxEffect);
256        if (NULL == enableLevel) {
257            result = SL_RESULT_PARAMETER_INVALID;
258        } else {
259            result = SL_RESULT_SUCCESS;
260            // EnableEffectSend is exclusive, so this has to be also
261            interface_lock_exclusive(this);
262            enableLevel->mSendLevel = sendLevel;
263#if defined(ANDROID) && !defined(USE_BACKPORT)
264            CAudioPlayer *ap = (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(this)) ?
265                    (CAudioPlayer *) this->mThis : NULL;
266            if (NULL != ap) {
267                // the send level set here is the total energy on the aux bus, so it must take
268                // into account the player volume level
269                result = android_fxSend_setSendLevel(ap, sendLevel + ap->mVolume.mLevel);
270            }
271#endif
272            interface_unlock_exclusive(this);
273
274        }
275    }
276
277    SL_LEAVE_INTERFACE
278}
279
280
281static SLresult IEffectSend_GetSendLevel(SLEffectSendItf self, const void *pAuxEffect,
282    SLmillibel *pSendLevel)
283{
284    SL_ENTER_INTERFACE
285
286    if (NULL == pSendLevel) {
287        result = SL_RESULT_PARAMETER_INVALID;
288    } else {
289        IEffectSend *this = (IEffectSend *) self;
290        struct EnableLevel *enableLevel = getEnableLevel(this, pAuxEffect);
291        if (NULL == enableLevel) {
292            result = SL_RESULT_PARAMETER_INVALID;
293        } else {
294            interface_lock_shared(this);
295            SLmillibel sendLevel = enableLevel->mSendLevel;
296            interface_unlock_shared(this);
297            *pSendLevel = sendLevel;
298            result = SL_RESULT_SUCCESS;
299        }
300    }
301
302    SL_LEAVE_INTERFACE
303}
304
305
306static const struct SLEffectSendItf_ IEffectSend_Itf = {
307    IEffectSend_EnableEffectSend,
308    IEffectSend_IsEnabled,
309    IEffectSend_SetDirectLevel,
310    IEffectSend_GetDirectLevel,
311    IEffectSend_SetSendLevel,
312    IEffectSend_GetSendLevel
313};
314
315void IEffectSend_init(void *self)
316{
317    IEffectSend *this = (IEffectSend *) self;
318    this->mItf = &IEffectSend_Itf;
319    struct EnableLevel *enableLevel = this->mEnableLevels;
320    unsigned aux;
321    for (aux = 0; aux < AUX_MAX; ++aux, ++enableLevel) {
322        enableLevel->mEnable = SL_BOOLEAN_FALSE;
323        enableLevel->mSendLevel = SL_MILLIBEL_MIN;
324    }
325}
326