xref: /aosp_15_r20/frameworks/av/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1 /*
2  * Copyright (C) 2020 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 #define LOG_TAG "EffectHG"
18 //#define LOG_NDEBUG 0
19 #include <utils/Log.h>
20 
21 #include "EffectHapticGenerator.h"
22 
23 #include <algorithm>
24 #include <memory>
25 #include <sstream>
26 #include <string>
27 #include <utility>
28 
29 #include <errno.h>
30 #include <inttypes.h>
31 
32 #include <android-base/parsedouble.h>
33 #include <android-base/properties.h>
34 #include <audio_effects/effect_hapticgenerator.h>
35 #include <audio_utils/format.h>
36 #include <audio_utils/safe_math.h>
37 #include <system/audio.h>
38 #include <system/audio_effects/audio_effects_utils.h>
39 
40 static constexpr float DEFAULT_RESONANT_FREQUENCY = 150.0f;
41 static constexpr float DEFAULT_BSF_ZERO_Q = 8.0f;
42 static constexpr float DEFAULT_BSF_POLE_Q = 4.0f;
43 static constexpr float DEFAULT_DISTORTION_OUTPUT_GAIN = 1.5f;
44 
45 using android::effect::utils::EffectParamReader;
46 
47 // This is the only symbol that needs to be exported
48 __attribute__ ((visibility ("default")))
49 audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
50         .tag = AUDIO_EFFECT_LIBRARY_TAG,
51         .version = EFFECT_LIBRARY_API_VERSION,
52         .name = "HapticGenerator Library",
53         .implementor = "The Android Open Source Project",
54         .create_effect = android::audio_effect::haptic_generator::HapticGeneratorLib_Create,
55         .release_effect = android::audio_effect::haptic_generator::HapticGeneratorLib_Release,
56         .get_descriptor = android::audio_effect::haptic_generator::HapticGeneratorLib_GetDescriptor,
57 };
58 
59 namespace android::audio_effect::haptic_generator {
60 
61 // effect_handle_t interface implementation for haptic generator effect
62 const struct effect_interface_s gHapticGeneratorInterface = {
63         HapticGenerator_Process,
64         HapticGenerator_Command,
65         HapticGenerator_GetDescriptor,
66         nullptr /* no process_reverse function, no reference stream needed */
67 };
68 
69 //-----------------------------------------------------------------------------
70 // Effect Descriptor
71 //-----------------------------------------------------------------------------
72 
73 // UUIDs for effect types have been generated from http://www.itu.int/ITU-T/asn1/uuid.html
74 // Haptic Generator
75 static const effect_descriptor_t gHgDescriptor = {
76         FX_IID_HAPTICGENERATOR_, // type
77         {0x97c4acd1, 0x8b82, 0x4f2f, 0x832e, {0xc2, 0xfe, 0x5d, 0x7a, 0x99, 0x31}}, // uuid
78         EFFECT_CONTROL_API_VERSION,
79         EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
80         0, // FIXME what value should be reported? // cpu load
81         0, // FIXME what value should be reported? // memory usage
82         "Haptic Generator",
83         "The Android Open Source Project"
84 };
85 
86 //-----------------------------------------------------------------------------
87 // Internal functions
88 //-----------------------------------------------------------------------------
89 
90 namespace {
91 
getFloatProperty(const std::string & key,float defaultValue)92 float getFloatProperty(const std::string& key, float defaultValue) {
93     float result;
94     std::string value = android::base::GetProperty(key, "");
95     if (!value.empty() && android::base::ParseFloat(value, &result)) {
96         return result;
97     }
98     return defaultValue;
99 }
100 
hapticParamToString(const struct HapticGeneratorParam & param)101 std::string hapticParamToString(const struct HapticGeneratorParam& param) {
102     std::stringstream ss;
103     ss << "\t\tHapticGenerator Parameters:\n";
104     ss << "\t\t- resonant frequency: " << param.resonantFrequency << '\n';
105     ss << "\t\t- bpf Q: " << param.bpfQ << '\n';
106     ss << "\t\t- slow env normalization power: " << param.slowEnvNormalizationPower << '\n';
107     ss << "\t\t- bsf zero Q: " << param.bsfZeroQ << '\n';
108     ss << "\t\t- bsf pole Q: " << param.bsfPoleQ << '\n';
109     ss << "\t\t- distortion corner frequency: " << param.distortionCornerFrequency << '\n';
110     ss << "\t\t- distortion input gain: " << param.distortionInputGain << '\n';
111     ss << "\t\t- distortion cube threshold: " << param.distortionCubeThreshold << '\n';
112     ss << "\t\t- distortion output gain: " << param.distortionOutputGain << '\n';
113     return ss.str();
114 }
115 
hapticSettingToString(const struct HapticGeneratorParam & param)116 std::string hapticSettingToString(const struct HapticGeneratorParam& param) {
117     std::stringstream ss;
118     ss << "\t\tHaptic setting:\n";
119     ss << "\t\t- tracks intensity map:\n";
120     for (const auto&[id, hapticScale] : param.id2HapticScale) {
121         ss << "\t\t\t- id=" << id << ", hapticLevel=" << (int) hapticScale.getLevel()
122            << ", adaptiveScaleFactor=" << hapticScale.getAdaptiveScaleFactor();
123     }
124     ss << "\t\t- max scale level: " << (int) param.maxHapticScale.getLevel() << '\n';
125     ss << "\t\t- max haptic amplitude: " << param.maxHapticAmplitude << '\n';
126     return ss.str();
127 }
128 
HapticGenerator_Init(struct HapticGeneratorContext * context)129 int HapticGenerator_Init(struct HapticGeneratorContext *context) {
130     context->itfe = &gHapticGeneratorInterface;
131 
132     context->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
133     context->config.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
134     context->config.inputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
135     context->config.inputCfg.samplingRate = 0;
136     context->config.inputCfg.bufferProvider.getBuffer = nullptr;
137     context->config.inputCfg.bufferProvider.releaseBuffer = nullptr;
138     context->config.inputCfg.bufferProvider.cookie = nullptr;
139     context->config.inputCfg.mask = EFFECT_CONFIG_ALL;
140     context->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
141     context->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
142     context->config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
143     context->config.outputCfg.samplingRate = 0;
144     context->config.outputCfg.bufferProvider.getBuffer = nullptr;
145     context->config.outputCfg.bufferProvider.releaseBuffer = nullptr;
146     context->config.outputCfg.bufferProvider.cookie = nullptr;
147     context->config.outputCfg.mask = EFFECT_CONFIG_ALL;
148 
149     memset(context->param.hapticChannelSource, 0, sizeof(context->param.hapticChannelSource));
150     context->param.hapticChannelCount = 0;
151     context->param.audioChannelCount = 0;
152     context->param.maxHapticScale = os::HapticScale::mute();
153 
154     context->param.resonantFrequency = DEFAULT_RESONANT_FREQUENCY;
155     context->param.bpfQ = 1.0f;
156     context->param.slowEnvNormalizationPower = -0.8f;
157     context->param.bsfZeroQ = DEFAULT_BSF_ZERO_Q;
158     context->param.bsfPoleQ = DEFAULT_BSF_POLE_Q;
159     context->param.distortionCornerFrequency = 300.0f;
160     context->param.distortionInputGain = 0.3f;
161     context->param.distortionCubeThreshold = 0.1f;
162     context->param.distortionOutputGain = getFloatProperty(
163             "vendor.audio.hapticgenerator.distortion.output.gain", DEFAULT_DISTORTION_OUTPUT_GAIN);
164     ALOGD("%s\n%s", __func__, hapticParamToString(context->param).c_str());
165 
166     context->state = HAPTICGENERATOR_STATE_INITIALIZED;
167     return 0;
168 }
169 
addBiquadFilter(std::vector<std::function<void (float *,const float *,size_t)>> & processingChain,struct HapticGeneratorProcessorsRecord & processorsRecord,std::shared_ptr<HapticBiquadFilter> filter)170 void addBiquadFilter(
171         std::vector<std::function<void(float *, const float *, size_t)>> &processingChain,
172         struct HapticGeneratorProcessorsRecord &processorsRecord,
173         std::shared_ptr<HapticBiquadFilter> filter) {
174     // The process chain captures the shared pointer of the filter in lambda.
175     // The process record will keep a shared pointer to the filter so that it is possible to access
176     // the filter outside of the process chain.
177     processorsRecord.filters.push_back(filter);
178     processingChain.push_back([filter](float *out, const float *in, size_t frameCount) {
179             filter->process(out, in, frameCount);
180     });
181 }
182 
183 /**
184  * \brief build haptic generator processing chain.
185  *
186  * \param processingChain
187  * \param processorsRecord a structure to cache all the shared pointers for processors
188  * \param sampleRate the audio sampling rate. Use a float here as it may be used to create filters
189  * \param channelCount haptic channel count
190  */
HapticGenerator_buildProcessingChain(std::vector<std::function<void (float *,const float *,size_t)>> & processingChain,struct HapticGeneratorProcessorsRecord & processorsRecord,float sampleRate,const struct HapticGeneratorParam * param)191 void HapticGenerator_buildProcessingChain(
192         std::vector<std::function<void(float*, const float*, size_t)>>& processingChain,
193         struct HapticGeneratorProcessorsRecord& processorsRecord, float sampleRate,
194         const struct HapticGeneratorParam* param) {
195     const size_t channelCount = param->hapticChannelCount;
196     float highPassCornerFrequency = 50.0f;
197     auto hpf = createHPF2(highPassCornerFrequency, sampleRate, channelCount);
198     addBiquadFilter(processingChain, processorsRecord, hpf);
199     float lowPassCornerFrequency = 9000.0f;
200     auto lpf = createLPF2(lowPassCornerFrequency, sampleRate, channelCount);
201     addBiquadFilter(processingChain, processorsRecord, lpf);
202 
203     auto ramp = std::make_shared<Ramp>(channelCount);  // ramp = half-wave rectifier.
204     // The process chain captures the shared pointer of the ramp in lambda. It will be the only
205     // reference to the ramp.
206     // The process record will keep a weak pointer to the ramp so that it is possible to access
207     // the ramp outside of the process chain.
208     processorsRecord.ramps.push_back(ramp);
209     processingChain.push_back([ramp](float *out, const float *in, size_t frameCount) {
210             ramp->process(out, in, frameCount);
211     });
212 
213     highPassCornerFrequency = 60.0f;
214     hpf = createHPF2(highPassCornerFrequency, sampleRate, channelCount);
215     addBiquadFilter(processingChain, processorsRecord, hpf);
216     lowPassCornerFrequency = 700.0f;
217     lpf = createLPF2(lowPassCornerFrequency, sampleRate, channelCount);
218     addBiquadFilter(processingChain, processorsRecord, lpf);
219 
220     lowPassCornerFrequency = 400.0f;
221     lpf = createLPF2(lowPassCornerFrequency, sampleRate, channelCount);
222     addBiquadFilter(processingChain, processorsRecord, lpf);
223     lowPassCornerFrequency = 500.0f;
224     lpf = createLPF2(lowPassCornerFrequency, sampleRate, channelCount);
225     addBiquadFilter(processingChain, processorsRecord, lpf);
226 
227     auto bpf = createBPF(param->resonantFrequency, param->bpfQ, sampleRate, channelCount);
228     processorsRecord.bpf = bpf;
229     addBiquadFilter(processingChain, processorsRecord, bpf);
230 
231     float normalizationPower = param->slowEnvNormalizationPower;
232     // The process chain captures the shared pointer of the slow envelope in lambda. It will
233     // be the only reference to the slow envelope.
234     // The process record will keep a weak pointer to the slow envelope so that it is possible
235     // to access the slow envelope outside of the process chain.
236     auto slowEnv = std::make_shared<SlowEnvelope>(  // SlowEnvelope = partial normalizer, or AGC.
237             5.0f /*envCornerFrequency*/, sampleRate, normalizationPower,
238             0.01f /*envOffset*/, channelCount);
239     processorsRecord.slowEnvs.push_back(slowEnv);
240     processingChain.push_back([slowEnv](float *out, const float *in, size_t frameCount) {
241             slowEnv->process(out, in, frameCount);
242     });
243 
244 
245     auto bsf = createBSF(
246             param->resonantFrequency, param->bsfZeroQ, param->bsfPoleQ, sampleRate, channelCount);
247     processorsRecord.bsf = bsf;
248     addBiquadFilter(processingChain, processorsRecord, bsf);
249 
250     // The process chain captures the shared pointer of the Distortion in lambda. It will
251     // be the only reference to the Distortion.
252     // The process record will keep a weak pointer to the Distortion so that it is possible
253     // to access the Distortion outside of the process chain.
254     auto distortion = std::make_shared<Distortion>(
255             param->distortionCornerFrequency, sampleRate, param->distortionInputGain,
256             param->distortionCubeThreshold, param->distortionOutputGain, channelCount);
257     processorsRecord.distortions.push_back(distortion);
258     processingChain.push_back([distortion](float *out, const float *in, size_t frameCount) {
259             distortion->process(out, in, frameCount);
260     });
261 }
262 
HapticGenerator_Configure(struct HapticGeneratorContext * context,effect_config_t * config)263 int HapticGenerator_Configure(struct HapticGeneratorContext *context, effect_config_t *config) {
264     if (config->inputCfg.samplingRate != config->outputCfg.samplingRate ||
265         config->inputCfg.format != config->outputCfg.format ||
266         config->inputCfg.format != AUDIO_FORMAT_PCM_FLOAT ||
267         config->inputCfg.channels != config->outputCfg.channels ||
268         config->inputCfg.buffer.frameCount != config->outputCfg.buffer.frameCount) {
269         return -EINVAL;
270     }
271     if (&context->config != config) {
272         context->processingChain.clear();
273         context->processorsRecord.filters.clear();
274         context->processorsRecord.ramps.clear();
275         context->processorsRecord.slowEnvs.clear();
276         context->processorsRecord.distortions.clear();
277         memcpy(&context->config, config, sizeof(effect_config_t));
278         context->param.audioChannelCount = audio_channel_count_from_out_mask(
279                 ((audio_channel_mask_t) config->inputCfg.channels) & ~AUDIO_CHANNEL_HAPTIC_ALL);
280         context->param.hapticChannelCount = audio_channel_count_from_out_mask(
281                 ((audio_channel_mask_t) config->outputCfg.channels) & AUDIO_CHANNEL_HAPTIC_ALL);
282         ALOG_ASSERT(context->param.hapticChannelCount <= 2,
283                     "haptic channel count(%zu) is too large",
284                     context->param.hapticChannelCount);
285         context->audioDataBytesPerFrame = audio_bytes_per_frame(
286                 context->param.audioChannelCount, (audio_format_t) config->inputCfg.format);
287         for (size_t i = 0; i < context->param.hapticChannelCount; ++i) {
288             // By default, use the first audio channel to generate haptic channels.
289             context->param.hapticChannelSource[i] = 0;
290         }
291 
292         HapticGenerator_buildProcessingChain(context->processingChain,
293                                              context->processorsRecord,
294                                              config->inputCfg.samplingRate,
295                                              &context->param);
296     }
297     return 0;
298 }
299 
HapticGenerator_Reset(struct HapticGeneratorContext * context)300 int HapticGenerator_Reset(struct HapticGeneratorContext *context) {
301     for (auto& filter : context->processorsRecord.filters) {
302         filter->clear();
303     }
304     for (auto& slowEnv : context->processorsRecord.slowEnvs) {
305         slowEnv->clear();
306     }
307     for (auto& distortion : context->processorsRecord.distortions) {
308         distortion->clear();
309     }
310     return 0;
311 }
312 
HapticGenerator_SetParameter(struct HapticGeneratorContext * context,effect_param_t * param)313 int HapticGenerator_SetParameter(struct HapticGeneratorContext *context, effect_param_t* param) {
314     if (param == nullptr) {
315         ALOGE("%s invalid effect_param_t is nullptr", __func__);
316         return -EINVAL;
317     }
318     int32_t paramType;
319     EffectParamReader reader(*param);
320     reader.readFromParameter(&paramType);
321 
322     switch (paramType) {
323     case HG_PARAM_HAPTIC_INTENSITY: {
324         if (param->vsize != (sizeof(int32_t) + sizeof(os::HapticScale))) {
325             ALOGE("%s invalid haptic intensity param size %s", __func__, reader.toString().c_str());
326             return -EINVAL;
327         }
328         int32_t id, scaleLevel;
329         float scaleFactor, adaptiveScaleFactor;
330         if (reader.readFromValue(&id) != OK || reader.readFromValue(&scaleLevel) != OK ||
331             reader.readFromValue(&scaleFactor) != OK ||
332             reader.readFromValue(&adaptiveScaleFactor) != OK) {
333             ALOGE("%s error reading haptic intensity %s", __func__, reader.toString().c_str());
334             return -EINVAL;
335         }
336         os::HapticScale hapticScale(static_cast<os::HapticLevel>(scaleLevel), scaleFactor,
337                                     adaptiveScaleFactor);
338         ALOGD("Updating haptic scale, %s", hapticScale.toString().c_str());
339         if (hapticScale.isScaleMute()) {
340             context->param.id2HapticScale.erase(id);
341         } else {
342             context->param.id2HapticScale.emplace(id, hapticScale);
343         }
344         context->param.maxHapticScale = hapticScale;
345         for (const auto&[_, scale] : context->param.id2HapticScale) {
346             // TODO(b/360314386): update to use new scale factors
347             if (scale.getLevel() > context->param.maxHapticScale.getLevel()) {
348                 context->param.maxHapticScale = scale;
349             }
350         }
351         break;
352     }
353     case HG_PARAM_VIBRATOR_INFO: {
354         if (param->vsize != (3 * sizeof(float))) {
355             ALOGE("%s invalid vibrator info param size %s", __func__, reader.toString().c_str());
356             return -EINVAL;
357         }
358         float resonantFrequency, qFactor, maxAmplitude;
359         if (reader.readFromValue(&resonantFrequency) != OK ||
360             reader.readFromValue(&qFactor) != OK ||
361             reader.readFromValue(&maxAmplitude) != OK) {
362             ALOGE("%s error reading vibrator info %s", __func__, reader.toString().c_str());
363             return -EINVAL;
364         }
365         context->param.resonantFrequency =
366                 audio_utils::safe_isnan(resonantFrequency) ? DEFAULT_RESONANT_FREQUENCY
367                                                            : resonantFrequency;
368         context->param.bsfZeroQ = audio_utils::safe_isnan(qFactor) ? DEFAULT_BSF_ZERO_Q : qFactor;
369         context->param.bsfPoleQ = context->param.bsfZeroQ / 2.0f;
370         context->param.maxHapticAmplitude = maxAmplitude;
371         ALOGD("Updating vibrator info, resonantFrequency=%f, bsfZeroQ=%f, bsfPoleQ=%f, "
372               "maxHapticAmplitude=%f",
373               context->param.resonantFrequency, context->param.bsfZeroQ, context->param.bsfPoleQ,
374               context->param.maxHapticAmplitude);
375 
376         if (context->processorsRecord.bpf != nullptr) {
377             context->processorsRecord.bpf->setCoefficients(
378                     bpfCoefs(context->param.resonantFrequency,
379                              context->param.bpfQ,
380                              context->config.inputCfg.samplingRate));
381         }
382         if (context->processorsRecord.bsf != nullptr) {
383             context->processorsRecord.bsf->setCoefficients(
384                     bsfCoefs(context->param.resonantFrequency,
385                              context->param.bsfZeroQ,
386                              context->param.bsfPoleQ,
387                              context->config.inputCfg.samplingRate));
388         }
389         HapticGenerator_Reset(context);
390     } break;
391     default:
392         ALOGW("Unknown param: %d", paramType);
393         return -EINVAL;
394     }
395 
396     return 0;
397 }
398 
399 /**
400  * \brief run the processing chain to generate haptic data from audio data
401  *
402  * \param processingChain the processing chain for generating haptic data
403  * \param buf1 a buffer contains raw audio data
404  * \param buf2 a buffer that is large enough to keep all the data
405  * \param frameCount frame count of the data
406  * \return a pointer to the output buffer
407  */
HapticGenerator_runProcessingChain(const std::vector<std::function<void (float *,const float *,size_t)>> & processingChain,float * buf1,float * buf2,size_t frameCount)408 float* HapticGenerator_runProcessingChain(
409         const std::vector<std::function<void(float*, const float*, size_t)>>& processingChain,
410         float* buf1, float* buf2, size_t frameCount) {
411     float *in = buf1;
412     float *out = buf2;
413     for (const auto processingFunc : processingChain) {
414         processingFunc(out, in, frameCount);
415         std::swap(in, out);
416     }
417     return in;
418 }
419 
HapticGenerator_Dump(int32_t fd,const struct HapticGeneratorParam & param)420 void HapticGenerator_Dump(int32_t fd, const struct HapticGeneratorParam& param) {
421     dprintf(fd, "%s", hapticParamToString(param).c_str());
422     dprintf(fd, "%s", hapticSettingToString(param).c_str());
423 }
424 
425 } // namespace (anonymous)
426 
427 //-----------------------------------------------------------------------------
428 // Effect API Implementation
429 //-----------------------------------------------------------------------------
430 
431 /*--- Effect Library Interface Implementation ---*/
432 
HapticGeneratorLib_Create(const effect_uuid_t * uuid,int32_t sessionId __unused,int32_t ioId __unused,effect_handle_t * handle)433 int32_t HapticGeneratorLib_Create(const effect_uuid_t *uuid,
434                                   int32_t sessionId __unused,
435                                   int32_t ioId __unused,
436                                   effect_handle_t *handle) {
437     if (handle == nullptr || uuid == nullptr) {
438         return -EINVAL;
439     }
440 
441     if (memcmp(uuid, &gHgDescriptor.uuid, sizeof(*uuid)) != 0) {
442         return -EINVAL;
443     }
444 
445     HapticGeneratorContext *context = new HapticGeneratorContext;
446     HapticGenerator_Init(context);
447 
448     *handle = (effect_handle_t) context;
449     ALOGV("%s context is %p", __func__, context);
450     return 0;
451 }
452 
HapticGeneratorLib_Release(effect_handle_t handle)453 int32_t HapticGeneratorLib_Release(effect_handle_t handle) {
454     HapticGeneratorContext *context = (HapticGeneratorContext *) handle;
455     delete context;
456     return 0;
457 }
458 
HapticGeneratorLib_GetDescriptor(const effect_uuid_t * uuid,effect_descriptor_t * descriptor)459 int32_t HapticGeneratorLib_GetDescriptor(const effect_uuid_t *uuid,
460                                          effect_descriptor_t *descriptor) {
461 
462     if (descriptor == nullptr || uuid == nullptr) {
463         ALOGE("%s() called with NULL pointer", __func__);
464         return -EINVAL;
465     }
466 
467     if (memcmp(uuid, &gHgDescriptor.uuid, sizeof(*uuid)) == 0) {
468         *descriptor = gHgDescriptor;
469         return 0;
470     }
471 
472     return -EINVAL;
473 }
474 
475 /*--- Effect Control Interface Implementation ---*/
476 
HapticGenerator_Process(effect_handle_t self,audio_buffer_t * inBuffer,audio_buffer_t * outBuffer)477 int32_t HapticGenerator_Process(effect_handle_t self,
478                                 audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
479     HapticGeneratorContext *context = (HapticGeneratorContext *) self;
480 
481     if (inBuffer == nullptr || inBuffer->raw == nullptr
482             || outBuffer == nullptr || outBuffer->raw == nullptr) {
483         return 0;
484     }
485 
486     // The audio data must not be modified but just written to
487     // output buffer according the access mode.
488     size_t audioBytes = context->audioDataBytesPerFrame * inBuffer->frameCount;
489     size_t audioSampleCount = inBuffer->frameCount * context->param.audioChannelCount;
490     if (inBuffer->raw != outBuffer->raw) {
491         if (context->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
492             for (size_t i = 0; i < audioSampleCount; ++i) {
493                 outBuffer->f32[i] += inBuffer->f32[i];
494             }
495         } else {
496             memcpy(outBuffer->raw, inBuffer->raw, audioBytes);
497         }
498     }
499 
500     if (context->state != HAPTICGENERATOR_STATE_ACTIVE) {
501         ALOGE("State(%d) is not HAPTICGENERATOR_STATE_ACTIVE when calling %s",
502                 context->state, __func__);
503         return -ENODATA;
504     }
505 
506     if (context->param.maxHapticScale.isScaleMute()) {
507         // Haptic channels are muted, not need to generate haptic data.
508         return 0;
509     }
510 
511     // Resize buffer if the haptic sample count is greater than buffer size.
512     size_t hapticSampleCount = inBuffer->frameCount * context->param.hapticChannelCount;
513     if (hapticSampleCount > context->inputBuffer.size()) {
514         // The context->inputBuffer and context->outputBuffer must have the same size,
515         // which must be at least the haptic sample count.
516         context->inputBuffer.resize(hapticSampleCount);
517         context->outputBuffer.resize(hapticSampleCount);
518     }
519 
520     // Construct input buffer according to haptic channel source
521     for (size_t i = 0; i < inBuffer->frameCount; ++i) {
522         for (size_t j = 0; j < context->param.hapticChannelCount; ++j) {
523             context->inputBuffer[i * context->param.hapticChannelCount + j] =
524                     inBuffer->f32[i * context->param.audioChannelCount
525                             + context->param.hapticChannelSource[j]];
526         }
527     }
528 
529     float* hapticOutBuffer = HapticGenerator_runProcessingChain(
530             context->processingChain, context->inputBuffer.data(),
531             context->outputBuffer.data(), inBuffer->frameCount);
532         os::scaleHapticData(hapticOutBuffer, hapticSampleCount,
533                             context->param.maxHapticScale,
534                             context->param.maxHapticAmplitude);
535 
536     // For haptic data, the haptic playback thread will copy the data from effect input buffer,
537     // which contains haptic data at the end of the buffer, directly to sink buffer.
538     // In that case, copy haptic data to input buffer instead of output buffer.
539     // Note: this may not work with rpc/binder calls
540     memcpy_by_audio_format(static_cast<char*>(inBuffer->raw) + audioBytes,
541                            static_cast<audio_format_t>(context->config.outputCfg.format),
542                            hapticOutBuffer,
543                            AUDIO_FORMAT_PCM_FLOAT,
544                            hapticSampleCount);
545 
546     return 0;
547 }
548 
HapticGenerator_Command(effect_handle_t self,uint32_t cmdCode,uint32_t cmdSize,void * cmdData,uint32_t * replySize,void * replyData)549 int32_t HapticGenerator_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
550                                 void *cmdData, uint32_t *replySize, void *replyData) {
551     HapticGeneratorContext *context = (HapticGeneratorContext *) self;
552 
553     if (context == nullptr || context->state == HAPTICGENERATOR_STATE_UNINITIALIZED) {
554         return -EINVAL;
555     }
556 
557     ALOGV("HapticGenerator_Command command %u cmdSize %u", cmdCode, cmdSize);
558 
559     switch (cmdCode) {
560         case EFFECT_CMD_INIT:
561             if (replyData == nullptr || replySize == nullptr || *replySize != sizeof(int)) {
562                 return -EINVAL;
563             }
564             *(int *) replyData = HapticGenerator_Init(context);
565             break;
566 
567         case EFFECT_CMD_SET_CONFIG:
568             if (cmdData == nullptr || cmdSize != sizeof(effect_config_t)
569                 || replyData == nullptr || replySize == nullptr || *replySize != sizeof(int)) {
570                 return -EINVAL;
571             }
572             *(int *) replyData = HapticGenerator_Configure(
573                     context, (effect_config_t *) cmdData);
574             break;
575 
576         case EFFECT_CMD_RESET:
577             HapticGenerator_Reset(context);
578             break;
579 
580         case EFFECT_CMD_GET_PARAM:
581             ALOGV("HapticGenerator_Command EFFECT_CMD_GET_PARAM cmdData %p,"
582                   "*replySize %u, replyData: %p",
583                   cmdData, *replySize, replyData);
584             break;
585 
586         case EFFECT_CMD_SET_PARAM: {
587             ALOGV("HapticGenerator_Command EFFECT_CMD_SET_PARAM cmdSize %d cmdData %p, "
588                   "*replySize %u, replyData %p", cmdSize, cmdData,
589                   replySize ? *replySize : 0, replyData);
590             if (cmdData == nullptr || (cmdSize < (int) (sizeof(effect_param_t) + sizeof(int32_t)))
591                 || replyData == nullptr || replySize == nullptr ||
592                 *replySize != (int) sizeof(int32_t)) {
593                 return -EINVAL;
594             }
595             effect_param_t *cmd = (effect_param_t *) cmdData;
596             *(int *) replyData = HapticGenerator_SetParameter(context, cmd);
597         }
598             break;
599 
600         case EFFECT_CMD_ENABLE:
601             if (replyData == nullptr || replySize == nullptr || *replySize != sizeof(int)) {
602                 return -EINVAL;
603             }
604             if (context->state != HAPTICGENERATOR_STATE_INITIALIZED) {
605                 return -ENOSYS;
606             }
607             context->state = HAPTICGENERATOR_STATE_ACTIVE;
608             ALOGV("EFFECT_CMD_ENABLE() OK");
609             *(int *) replyData = 0;
610             break;
611 
612         case EFFECT_CMD_DISABLE:
613             if (replyData == nullptr || replySize == nullptr || *replySize != sizeof(int)) {
614                 return -EINVAL;
615             }
616             if (context->state != HAPTICGENERATOR_STATE_ACTIVE) {
617                 return -ENOSYS;
618             }
619             context->state = HAPTICGENERATOR_STATE_INITIALIZED;
620             ALOGV("EFFECT_CMD_DISABLE() OK");
621             *(int *) replyData = 0;
622             break;
623 
624         case EFFECT_CMD_SET_VOLUME:
625         case EFFECT_CMD_SET_DEVICE:
626         case EFFECT_CMD_SET_AUDIO_MODE:
627             break;
628 
629         case EFFECT_CMD_DUMP:
630             HapticGenerator_Dump(*(reinterpret_cast<int32_t*>(cmdData)), context->param);
631             break;
632 
633         default:
634             ALOGW("HapticGenerator_Command invalid command %u", cmdCode);
635             return -EINVAL;
636     }
637 
638     return 0;
639 }
640 
HapticGenerator_GetDescriptor(effect_handle_t self,effect_descriptor_t * descriptor)641 int32_t HapticGenerator_GetDescriptor(effect_handle_t self, effect_descriptor_t *descriptor) {
642     HapticGeneratorContext *context = (HapticGeneratorContext *) self;
643 
644     if (context == nullptr ||
645         context->state == HAPTICGENERATOR_STATE_UNINITIALIZED) {
646         return -EINVAL;
647     }
648 
649     memcpy(descriptor, &gHgDescriptor, sizeof(effect_descriptor_t));
650 
651     return 0;
652 }
653 
654 } // namespace android::audio_effect::haptic_generator
655