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