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