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