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