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