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