summaryrefslogtreecommitdiffstats
path: root/post_proc/virtualizer.c
blob: 1ab59278cc7398152cce141b9ad72a82f8892adb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
/*
 * Copyright (C) 2014 The Android Open Source Project
 * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
 * Not a Contribution.
 *
 * 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 "offload_effect_virtualizer"
//#define LOG_NDEBUG 0

#include <cutils/list.h>
#include <cutils/log.h>
#include <tinyalsa/asoundlib.h>
#include <sound/audio_effects.h>
#include <audio_effects/effect_virtualizer.h>

#include "effect_api.h"
#include "virtualizer.h"

/* Offload Virtualizer UUID: 509a4498-561a-4bea-b3b1-0002a5d5c51b */
const effect_descriptor_t virtualizer_descriptor = {
        {0x37cc2c00, 0xdddd, 0x11db, 0x8577, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
        {0x509a4498, 0x561a, 0x4bea, 0xb3b1, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid
        EFFECT_CONTROL_API_VERSION,
        (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_DEVICE_IND | EFFECT_FLAG_HW_ACC_TUNNEL),
        0, /* TODO */
        1,
        "MSM offload virtualizer",
        "The Android Open Source Project",
};

/*
 * Virtualizer operations
 */

int virtualizer_get_strength(virtualizer_context_t *context)
{
    ALOGV("%s: strength: %d", __func__, context->strength);
    return context->strength;
}

int virtualizer_set_strength(virtualizer_context_t *context, uint32_t strength)
{
    ALOGV("%s: strength: %d", __func__, strength);
    context->strength = strength;

    /*
     *  Zero strength is not equivalent to disable state as down mix
     *  is still happening for multichannel inputs.
     *  For better user experience, explicitly disable virtualizer module
     *  when strength is 0.
     */
    offload_virtualizer_set_enable_flag(&(context->offload_virt),
                                        ((strength > 0) && !(context->temp_disabled)) ?
                                        true : false);
    offload_virtualizer_set_strength(&(context->offload_virt), strength);
    if (context->ctl)
        offload_virtualizer_send_params(context->ctl, &context->offload_virt,
                                        OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG |
                                        OFFLOAD_SEND_VIRTUALIZER_STRENGTH);
    return 0;
}

/*
 *  Check if an audio device is supported by this implementation
 *
 *  [in]
 *  device    device that is intented for processing (e.g. for binaural vs transaural)
 *  [out]
 *  false     device is not applicable for effect
 *  true      device is applicable for effect
 */
bool virtualizer_is_device_supported(audio_devices_t device) {
    switch (device) {
    case AUDIO_DEVICE_OUT_SPEAKER:
    case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
    case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER:
#ifdef AFE_PROXY_ENABLED
    case AUDIO_DEVICE_OUT_PROXY:
#endif
    case AUDIO_DEVICE_OUT_AUX_DIGITAL:
    case AUDIO_DEVICE_OUT_USB_ACCESSORY:
    case AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET:
        return false;
    default :
        return true;
    }
}

/*
 *  Check if a channel mask + audio device is supported by this implementation
 *
 *  [in]
 *  channel_mask channel mask of input buffer
 *  device       device that is intented for processing (e.g. for binaural vs transaural)
 *  [out]
 *  false        if the configuration is not supported or it is unknown
 *  true         if the configuration is supported
 */
bool virtualizer_is_configuration_supported(audio_channel_mask_t channel_mask,
        audio_devices_t device) {
    uint32_t channelCount = audio_channel_count_from_out_mask(channel_mask);
    if ((channelCount == 0) || (channelCount > 2)) {
        return false;
    }

    return virtualizer_is_device_supported(device);
}

/*
 *  Force the virtualization mode to that of the given audio device
 *
 *  [in]
 *  context       effect engine context
 *  forced_device device whose virtualization mode we'll always use
 *  [out]
 *  -EINVAL       if the device is not supported or is unknown
 *  0             if the device is supported and the virtualization mode forced
 */
int virtualizer_force_virtualization_mode(virtualizer_context_t *context,
        audio_devices_t forced_device) {
    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;
    int status = 0;
    bool use_virt = false;
    int is_virt_enabled =
        offload_virtualizer_get_enable_flag(&(virt_ctxt->offload_virt));

    ALOGV("%s: ctxt %p, forcedDev=0x%x enabled=%d tmpDisabled=%d", __func__, virt_ctxt,
            forced_device, is_virt_enabled, virt_ctxt->temp_disabled);

    if (virtualizer_is_device_supported(forced_device) == false) {
        if (forced_device != AUDIO_DEVICE_NONE) {
            //forced device is not supported, make it behave as a reset of forced mode
            forced_device = AUDIO_DEVICE_NONE;
            // but return an error
            status = -EINVAL;
        }
    }

    if (forced_device == AUDIO_DEVICE_NONE) {
        // disabling forced virtualization mode:
        // verify whether the virtualization should be enabled or disabled
        if (virtualizer_is_device_supported(virt_ctxt->device)) {
            use_virt = (is_virt_enabled == true);
        }
        virt_ctxt->forced_device = AUDIO_DEVICE_NONE;
    } else {
        // forcing virtualization mode:
        // TODO: we assume device is supported, so hard coded a fixed one.
        virt_ctxt->forced_device = AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
        // TODO: only enable for a supported mode, when the effect is enabled
        use_virt = (is_virt_enabled == true);
    }

    if (use_virt) {
        if (virt_ctxt->temp_disabled == true) {
            if (effect_is_active(&virt_ctxt->common)) {
                offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), true);
                if (virt_ctxt->ctl)
                    offload_virtualizer_send_params(virt_ctxt->ctl,
                                                    &virt_ctxt->offload_virt,
                                                    OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG);
            }
            ALOGV("%s: re-enable VIRTUALIZER", __func__);
            virt_ctxt->temp_disabled = false;
        } else {
            ALOGV("%s: leaving VIRTUALIZER enabled", __func__);
        }
    } else {
        if (virt_ctxt->temp_disabled == false) {
            if (effect_is_active(&virt_ctxt->common)) {
                offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), false);
                if (virt_ctxt->ctl)
                    offload_virtualizer_send_params(virt_ctxt->ctl,
                                                    &virt_ctxt->offload_virt,
                                                    OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG);
            }
            ALOGV("%s: disable VIRTUALIZER", __func__);
            virt_ctxt->temp_disabled = true;
        } else {
            ALOGV("%s: leaving VIRTUALIZER disabled", __func__);
        }
    }

    ALOGV("after %s: ctxt %p, enabled=%d tmpDisabled=%d", __func__, virt_ctxt,
            is_virt_enabled, virt_ctxt->temp_disabled);

    return status;
}

/*
 *  Get the virtual speaker angles for a channel mask + audio device configuration
 *  which is guaranteed to be supported by this implementation
 *
 *  [in]
 *  channel_mask   the channel mask of the input to virtualize
 *  device         the type of device that affects the processing (e.g. for binaural vs transaural)
 *  [in/out]
 *  speaker_angles the array of integer where each speaker angle is written as a triplet in the
 *                 following format:
 *                  int32_t a bit mask with a single value selected for each speaker, following
 *                  the convention of the audio_channel_mask_t type
 *                  int32_t a value in degrees expressing the speaker azimuth, where 0 is in front
 *                  of the user, 180 behind, -90 to the left, 90 to the right of the user
 *                  int32_t a value in degrees expressing the speaker elevation, where 0 is the
 *                  horizontal plane, +90 is directly above the user, -90 below
 *
 */
void virtualizer_get_speaker_angles(audio_channel_mask_t channel_mask __unused,
        audio_devices_t device __unused, int32_t *speaker_angles) {
    // the channel count is guaranteed to be 1 or 2
    // the device is guaranteed to be of type headphone
    // this virtualizer is always 2in with speakers at -90 and 90deg of azimuth, 0deg of elevation
    *speaker_angles++ = (int32_t) AUDIO_CHANNEL_OUT_FRONT_LEFT;
    *speaker_angles++ = -90; // azimuth
    *speaker_angles++ = 0;   // elevation
    *speaker_angles++ = (int32_t) AUDIO_CHANNEL_OUT_FRONT_RIGHT;
    *speaker_angles++ = 90;  // azimuth
    *speaker_angles   = 0;   // elevation
}

/*
 *  Retrieve the current device whose processing mode is used by this effect
 *
 *  [out]
 *  AUDIO_DEVICE_NONE if the effect is not virtualizing
 *  or the device type if the effect is virtualizing
 */
audio_devices_t virtualizer_get_virtualization_mode(virtualizer_context_t *context) {
    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;
    audio_devices_t device = AUDIO_DEVICE_NONE;

    if ((offload_virtualizer_get_enable_flag(&(virt_ctxt->offload_virt)))
            && (virt_ctxt->temp_disabled == false)) {
        if (virt_ctxt->forced_device != AUDIO_DEVICE_NONE) {
            // virtualization mode is forced, return that device
            device = virt_ctxt->forced_device;
        } else {
            // no forced mode, return the current device
            device = virt_ctxt->device;
        }
    }
    ALOGV("%s: returning 0x%x", __func__, device);
    return device;
}

int virtualizer_get_parameter(effect_context_t *context, effect_param_t *p,
                              uint32_t *size)
{
    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;
    int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
    int32_t *param_tmp = (int32_t *)p->data;
    int32_t param = *param_tmp++;
    void *value = p->data + voffset;
    int i;

    ALOGV("%s", __func__);

    p->status = 0;

    switch (param) {
    case VIRTUALIZER_PARAM_STRENGTH_SUPPORTED:
        if (p->vsize < sizeof(uint32_t))
           p->status = -EINVAL;
        p->vsize = sizeof(uint32_t);
        break;
    case VIRTUALIZER_PARAM_STRENGTH:
        if (p->vsize < sizeof(int16_t))
           p->status = -EINVAL;
        p->vsize = sizeof(int16_t);
        break;
    case VIRTUALIZER_PARAM_VIRTUAL_SPEAKER_ANGLES:
        // return value size can only be interpreted as relative to input value,
        // deferring validity check to below
        break;
    case VIRTUALIZER_PARAM_VIRTUALIZATION_MODE:
        if (p->vsize != sizeof(uint32_t))
           p->status = -EINVAL;
        p->vsize = sizeof(uint32_t);
        break;
    default:
        p->status = -EINVAL;
    }

    *size = sizeof(effect_param_t) + voffset + p->vsize;

    if (p->status != 0)
        return 0;

    switch (param) {
    case VIRTUALIZER_PARAM_STRENGTH_SUPPORTED:
	ALOGV("%s: VIRTUALIZER_PARAM_STRENGTH_SUPPORTED", __func__);
        *(uint32_t *)value = 1;
        break;

    case VIRTUALIZER_PARAM_STRENGTH:
	ALOGV("%s: VIRTUALIZER_PARAM_STRENGTH", __func__);
        *(int16_t *)value = virtualizer_get_strength(virt_ctxt);
        break;

    case VIRTUALIZER_PARAM_VIRTUAL_SPEAKER_ANGLES:
    {
        const audio_channel_mask_t channel_mask = (audio_channel_mask_t) *param_tmp++;
        const audio_devices_t device = (audio_devices_t) *param_tmp;
        uint32_t channel_cnt = audio_channel_count_from_out_mask(channel_mask);

        if (p->vsize < 3 * channel_cnt * sizeof(int32_t)){
            p->status = -EINVAL;
            break;
        }
        // verify the configuration is supported
        if(virtualizer_is_configuration_supported(channel_mask, device)) {
            // configuration is supported, get the angles
            virtualizer_get_speaker_angles(channel_mask, device, (int32_t *)value);
        } else {
            p->status = -EINVAL;
        }

        break;
    }

    case VIRTUALIZER_PARAM_VIRTUALIZATION_MODE:
        *(uint32_t *)value  = (uint32_t) virtualizer_get_virtualization_mode(virt_ctxt);
        break;

    default:
        p->status = -EINVAL;
        break;
    }

    return 0;
}

int virtualizer_set_parameter(effect_context_t *context, effect_param_t *p,
                              uint32_t size __unused)
{
    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;
    int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
    void *value = p->data + voffset;
    int32_t *param_tmp = (int32_t *)p->data;
    int32_t param = *param_tmp++;
    uint32_t strength;

    ALOGV("%s", __func__);

    p->status = 0;

    switch (param) {
    case VIRTUALIZER_PARAM_STRENGTH:
	ALOGV("%s VIRTUALIZER_PARAM_STRENGTH", __func__);
        strength = (uint32_t)(*(int16_t *)value);
        virtualizer_set_strength(virt_ctxt, strength);
        break;
    case VIRTUALIZER_PARAM_FORCE_VIRTUALIZATION_MODE:
    {
        const audio_devices_t device = *(audio_devices_t *)value;
        if (0 != virtualizer_force_virtualization_mode(virt_ctxt, device)) {
            p->status = -EINVAL;
        }
        break;
    }
    default:
        p->status = -EINVAL;
        break;
    }

    return 0;
}

int virtualizer_set_device(effect_context_t *context, uint32_t device)
{
    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;

    ALOGV("%s: device: %d", __func__, device);
    virt_ctxt->device = device;

    if (virt_ctxt->forced_device == AUDIO_DEVICE_NONE) {
        // default case unless configuration is forced
        if (virtualizer_is_device_supported(device) == false) {
            if (!virt_ctxt->temp_disabled) {
                if (effect_is_active(&virt_ctxt->common)) {
                    offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), false);
                    if (virt_ctxt->ctl)
                        offload_virtualizer_send_params(virt_ctxt->ctl,
                                                        &virt_ctxt->offload_virt,
                                                        OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG);
                }
                virt_ctxt->temp_disabled = true;
                ALOGI("%s: ctxt %p, disabled based on device", __func__, virt_ctxt);
            }
        } else {
            if (virt_ctxt->temp_disabled) {
                if (effect_is_active(&virt_ctxt->common)) {
                    offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), true);
                    if (virt_ctxt->ctl)
                        offload_virtualizer_send_params(virt_ctxt->ctl,
                                                        &virt_ctxt->offload_virt,
                                                        OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG);
                }
                virt_ctxt->temp_disabled = false;
            }
        }
    }
    // else virtualization mode is forced to a certain device, nothing to do

    offload_virtualizer_set_device(&(virt_ctxt->offload_virt), device);
    return 0;
}

int virtualizer_reset(effect_context_t *context)
{
    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;

    return 0;
}

int virtualizer_init(effect_context_t *context)
{
    ALOGV("%s", __func__);
    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;

    context->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
    context->config.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
    context->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
    context->config.inputCfg.samplingRate = 44100;
    context->config.inputCfg.bufferProvider.getBuffer = NULL;
    context->config.inputCfg.bufferProvider.releaseBuffer = NULL;
    context->config.inputCfg.bufferProvider.cookie = NULL;
    context->config.inputCfg.mask = EFFECT_CONFIG_ALL;
    context->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
    context->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
    context->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
    context->config.outputCfg.samplingRate = 44100;
    context->config.outputCfg.bufferProvider.getBuffer = NULL;
    context->config.outputCfg.bufferProvider.releaseBuffer = NULL;
    context->config.outputCfg.bufferProvider.cookie = NULL;
    context->config.outputCfg.mask = EFFECT_CONFIG_ALL;

    set_config(context, &context->config);

    virt_ctxt->temp_disabled = false;
    virt_ctxt->forced_device = AUDIO_DEVICE_NONE;
    virt_ctxt->device = AUDIO_DEVICE_NONE;
    memset(&(virt_ctxt->offload_virt), 0, sizeof(struct virtualizer_params));

    return 0;
}

int virtualizer_enable(effect_context_t *context)
{
    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;

    ALOGV("%s", __func__);

    if (!offload_virtualizer_get_enable_flag(&(virt_ctxt->offload_virt)) &&
        !(virt_ctxt->temp_disabled)) {
        offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), true);
        if (virt_ctxt->ctl && virt_ctxt->strength)
            offload_virtualizer_send_params(virt_ctxt->ctl,
                                          &virt_ctxt->offload_virt,
                                          OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG |
                                          OFFLOAD_SEND_BASSBOOST_STRENGTH);
    }
    return 0;
}

int virtualizer_disable(effect_context_t *context)
{
    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;

    ALOGV("%s", __func__);
    if (offload_virtualizer_get_enable_flag(&(virt_ctxt->offload_virt))) {
        offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), false);
        if (virt_ctxt->ctl)
            offload_virtualizer_send_params(virt_ctxt->ctl,
                                          &virt_ctxt->offload_virt,
                                          OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG);
    }
    return 0;
}

int virtualizer_start(effect_context_t *context, output_context_t *output)
{
    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;

    ALOGV("%s", __func__);
    virt_ctxt->ctl = output->ctl;
    if (offload_virtualizer_get_enable_flag(&(virt_ctxt->offload_virt)))
        if (virt_ctxt->ctl)
            offload_virtualizer_send_params(virt_ctxt->ctl, &virt_ctxt->offload_virt,
                                          OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG |
                                          OFFLOAD_SEND_VIRTUALIZER_STRENGTH);
    return 0;
}

int virtualizer_stop(effect_context_t *context, output_context_t *output __unused)
{
    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;

    ALOGV("%s", __func__);
    virt_ctxt->ctl = NULL;
    return 0;
}