/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "offload_effect_equalizer" //#define LOG_NDEBUG 0 #include <cutils/list.h> #include <cutils/log.h> #include <tinyalsa/asoundlib.h> #include <sound/audio_effects.h> #include <audio_effects/effect_equalizer.h> #include "effect_api.h" #include "equalizer.h" /* Offload equalizer UUID: a0dac280-401c-11e3-9379-0002a5d5c51b */ const effect_descriptor_t equalizer_descriptor = { {0x0bed4300, 0xddd6, 0x11db, 0x8f34, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type {0xa0dac280, 0x401c, 0x11e3, 0x9379, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid EFFECT_CONTROL_API_VERSION, (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_HW_ACC_TUNNEL | EFFECT_FLAG_VOLUME_CTRL), 0, /* TODO */ 1, "MSM offload equalizer", "The Android Open Source Project", }; static const char *equalizer_preset_names[] = { "Normal", "Classical", "Dance", "Flat", "Folk", "Heavy Metal", "Hip Hop", "Jazz", "Pop", "Rock" }; static const uint32_t equalizer_band_freq_range[NUM_EQ_BANDS][2] = { {30000, 120000}, {120001, 460000}, {460001, 1800000}, {1800001, 7000000}, {7000001, 20000000}}; static const int16_t equalizer_band_presets_level[] = { 3, 0, 0, 0, 3, /* Normal Preset */ 5, 3, -2, 4, 4, /* Classical Preset */ 6, 0, 2, 4, 1, /* Dance Preset */ 0, 0, 0, 0, 0, /* Flat Preset */ 3, 0, 0, 2, -1, /* Folk Preset */ 4, 1, 9, 3, 0, /* Heavy Metal Preset */ 5, 3, 0, 1, 3, /* Hip Hop Preset */ 4, 2, -2, 2, 5, /* Jazz Preset */ -1, 2, 5, 1, -2, /* Pop Preset */ 5, 3, -1, 3, 5}; /* Rock Preset */ const uint16_t equalizer_band_presets_freq[NUM_EQ_BANDS] = { 60, /* Frequencies in Hz */ 230, 910, 3600, 14000 }; /* * Equalizer operations */ int equalizer_get_band_level(equalizer_context_t *context, int32_t band) { ALOGV("%s: band: %d level: %d", __func__, band, context->band_levels[band] * 100); return context->band_levels[band] * 100; } int equalizer_set_band_level(equalizer_context_t *context, int32_t band, int32_t level) { ALOGV("%s: band: %d, level: %d", __func__, band, level); if (level > 0) { level = (int)((level+50)/100); } else { level = (int)((level-50)/100); } context->band_levels[band] = level; context->preset = PRESET_CUSTOM; offload_eq_set_preset(&(context->offload_eq), PRESET_CUSTOM); offload_eq_set_bands_level(&(context->offload_eq), NUM_EQ_BANDS, equalizer_band_presets_freq, context->band_levels); if (context->ctl) offload_eq_send_params(context->ctl, &context->offload_eq, OFFLOAD_SEND_EQ_ENABLE_FLAG | OFFLOAD_SEND_EQ_BANDS_LEVEL); return 0; } int equalizer_get_center_frequency(equalizer_context_t *context __unused, int32_t band) { ALOGV("%s: band: %d", __func__, band); return (equalizer_band_freq_range[band][0] + equalizer_band_freq_range[band][1]) / 2; } int equalizer_get_band_freq_range(equalizer_context_t *context __unused, int32_t band, uint32_t *low, uint32_t *high) { ALOGV("%s: band: %d", __func__, band); *low = equalizer_band_freq_range[band][0]; *high = equalizer_band_freq_range[band][1]; return 0; } int equalizer_get_band(equalizer_context_t *context __unused, uint32_t freq) { int i; ALOGV("%s: freq: %d", __func__, freq); for (i = 0; i < NUM_EQ_BANDS; i++) { if (freq <= equalizer_band_freq_range[i][1]) { return i; } } return NUM_EQ_BANDS - 1; } int equalizer_get_preset(equalizer_context_t *context) { ALOGV("%s: preset: %d", __func__, context->preset); return context->preset; } int equalizer_set_preset(equalizer_context_t *context, int preset) { int i; ALOGV("%s: preset: %d", __func__, preset); context->preset = preset; for (i=0; i<NUM_EQ_BANDS; i++) context->band_levels[i] = equalizer_band_presets_level[i + preset * NUM_EQ_BANDS]; offload_eq_set_preset(&(context->offload_eq), preset); offload_eq_set_bands_level(&(context->offload_eq), NUM_EQ_BANDS, equalizer_band_presets_freq, context->band_levels); if(context->ctl) offload_eq_send_params(context->ctl, &context->offload_eq, OFFLOAD_SEND_EQ_ENABLE_FLAG | OFFLOAD_SEND_EQ_PRESET); return 0; } const char * equalizer_get_preset_name(equalizer_context_t *context __unused, int32_t preset) { ALOGV("%s: preset: %s", __func__, equalizer_preset_names[preset]); if (preset == PRESET_CUSTOM) { return "Custom"; } else { return equalizer_preset_names[preset]; } } int equalizer_get_num_presets(equalizer_context_t *context __unused) { ALOGV("%s: presets_num: %zu", __func__, sizeof(equalizer_preset_names)/sizeof(char *)); return sizeof(equalizer_preset_names)/sizeof(char *); } int equalizer_get_parameter(effect_context_t *context, effect_param_t *p, uint32_t *size) { equalizer_context_t *eq_ctxt = (equalizer_context_t *)context; int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t); int32_t *param_tmp = (int32_t *)p->data; int32_t param = *param_tmp++; int32_t param2; char *name; void *value = p->data + voffset; int i; ALOGV("%s", __func__); p->status = 0; switch (param) { case EQ_PARAM_NUM_BANDS: case EQ_PARAM_CUR_PRESET: case EQ_PARAM_GET_NUM_OF_PRESETS: case EQ_PARAM_BAND_LEVEL: case EQ_PARAM_GET_BAND: if (p->vsize < sizeof(int16_t)) p->status = -EINVAL; p->vsize = sizeof(int16_t); break; case EQ_PARAM_LEVEL_RANGE: if (p->vsize < 2 * sizeof(int16_t)) p->status = -EINVAL; p->vsize = 2 * sizeof(int16_t); break; case EQ_PARAM_BAND_FREQ_RANGE: if (p->vsize < 2 * sizeof(int32_t)) p->status = -EINVAL; p->vsize = 2 * sizeof(int32_t); break; case EQ_PARAM_CENTER_FREQ: if (p->vsize < sizeof(int32_t)) p->status = -EINVAL; p->vsize = sizeof(int32_t); break; case EQ_PARAM_GET_PRESET_NAME: break; case EQ_PARAM_PROPERTIES: if (p->vsize < (2 + NUM_EQ_BANDS) * sizeof(uint16_t)) p->status = -EINVAL; p->vsize = (2 + NUM_EQ_BANDS) * sizeof(uint16_t); break; default: p->status = -EINVAL; } *size = sizeof(effect_param_t) + voffset + p->vsize; if (p->status != 0) return 0; switch (param) { case EQ_PARAM_NUM_BANDS: ALOGV("%s: EQ_PARAM_NUM_BANDS", __func__); *(uint16_t *)value = (uint16_t)NUM_EQ_BANDS; break; case EQ_PARAM_LEVEL_RANGE: ALOGV("%s: EQ_PARAM_LEVEL_RANGE", __func__); *(int16_t *)value = -1500; *((int16_t *)value + 1) = 1500; break; case EQ_PARAM_BAND_LEVEL: ALOGV("%s: EQ_PARAM_BAND_LEVEL", __func__); param2 = *param_tmp; if (param2 < 0 || param2 >= NUM_EQ_BANDS) { p->status = -EINVAL; if (param2 < 0) { android_errorWriteLog(0x534e4554, "32438598"); ALOGW("\tERROR EQ_PARAM_BAND_LEVEL band %d", param2); } break; } *(int16_t *)value = (int16_t)equalizer_get_band_level(eq_ctxt, param2); break; case EQ_PARAM_CENTER_FREQ: ALOGV("%s: EQ_PARAM_CENTER_FREQ", __func__); param2 = *param_tmp; if (param2 < 0 || param2 >= NUM_EQ_BANDS) { p->status = -EINVAL; if (param2 < 0) { android_errorWriteLog(0x534e4554, "32436341"); ALOGW("\tERROR EQ_PARAM_CENTER_FREQ band %d", param2); } break; } *(int32_t *)value = equalizer_get_center_frequency(eq_ctxt, param2); break; case EQ_PARAM_BAND_FREQ_RANGE: ALOGV("%s: EQ_PARAM_BAND_FREQ_RANGE", __func__); param2 = *param_tmp; if (param2 < 0 || param2 >= NUM_EQ_BANDS) { p->status = -EINVAL; if (param2 < 0) { android_errorWriteLog(0x534e4554, "32247948"); ALOGW("\tERROR EQ_PARAM_BAND_FREQ_RANGE band %d", param2); } break; } equalizer_get_band_freq_range(eq_ctxt, param2, (uint32_t *)value, ((uint32_t *)value + 1)); break; case EQ_PARAM_GET_BAND: ALOGV("%s: EQ_PARAM_GET_BAND", __func__); param2 = *param_tmp; *(uint16_t *)value = (uint16_t)equalizer_get_band(eq_ctxt, param2); break; case EQ_PARAM_CUR_PRESET: ALOGV("%s: EQ_PARAM_CUR_PRESET", __func__); *(uint16_t *)value = (uint16_t)equalizer_get_preset(eq_ctxt); break; case EQ_PARAM_GET_NUM_OF_PRESETS: ALOGV("%s: EQ_PARAM_GET_NUM_OF_PRESETS", __func__); *(uint16_t *)value = (uint16_t)equalizer_get_num_presets(eq_ctxt); break; case EQ_PARAM_GET_PRESET_NAME: ALOGV("%s: EQ_PARAM_GET_PRESET_NAME", __func__); param2 = *param_tmp; ALOGV("param2: %d", param2); if ((param2 < 0 && param2 != PRESET_CUSTOM) || param2 >= equalizer_get_num_presets(eq_ctxt)) { p->status = -EINVAL; if (param2 < 0) { android_errorWriteLog(0x534e4554, "32588016"); ALOGW("\tERROR EQ_PARAM_GET_PRESET_NAME preset %d", param2); } break; } if (p->vsize < 1) { p->status = -EINVAL; android_errorWriteLog(0x534e4554, "37536407"); break; } name = (char *)value; strlcpy(name, equalizer_get_preset_name(eq_ctxt, param2), p->vsize - 1); name[p->vsize - 1] = 0; p->vsize = strlen(name) + 1; break; case EQ_PARAM_PROPERTIES: { ALOGV("%s: EQ_PARAM_PROPERTIES", __func__); int16_t *prop = (int16_t *)value; prop[0] = (int16_t)equalizer_get_preset(eq_ctxt); prop[1] = (int16_t)NUM_EQ_BANDS; for (i = 0; i < NUM_EQ_BANDS; i++) { prop[2 + i] = (int16_t)equalizer_get_band_level(eq_ctxt, i); } } break; default: p->status = -EINVAL; break; } return 0; } int equalizer_set_parameter(effect_context_t *context, effect_param_t *p, uint32_t size __unused) { equalizer_context_t *eq_ctxt = (equalizer_context_t *)context; int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t); void *value = p->data + voffset; int32_t vsize = (int32_t) p->vsize; int32_t *param_tmp = (int32_t *)p->data; int32_t param = *param_tmp++; int32_t preset; int32_t band; int32_t level; int i; ALOGV("%s", __func__); p->status = 0; switch (param) { case EQ_PARAM_CUR_PRESET: ALOGV("EQ_PARAM_CUR_PRESET"); if (vsize < sizeof(int16_t)) { p->status = -EINVAL; break; } preset = (int32_t)(*(uint16_t *)value); if ((preset >= equalizer_get_num_presets(eq_ctxt)) || (preset < 0)) { p->status = -EINVAL; break; } equalizer_set_preset(eq_ctxt, preset); break; case EQ_PARAM_BAND_LEVEL: ALOGV("EQ_PARAM_BAND_LEVEL"); if (vsize < sizeof(int16_t)) { p->status = -EINVAL; break; } band = *param_tmp; level = (int32_t)(*(int16_t *)value); if (band < 0 || band >= NUM_EQ_BANDS) { p->status = -EINVAL; if (band < 0) { android_errorWriteLog(0x534e4554, "32585400"); ALOGW("\tERROR EQ_PARAM_BAND_LEVEL band %d", band); } break; } equalizer_set_band_level(eq_ctxt, band, level); break; case EQ_PARAM_PROPERTIES: { ALOGV("EQ_PARAM_PROPERTIES"); if (vsize < sizeof(int16_t)) { p->status = -EINVAL; break; } int16_t *prop = (int16_t *)value; if ((int)prop[0] >= equalizer_get_num_presets(eq_ctxt)) { p->status = -EINVAL; break; } if (prop[0] >= 0) { equalizer_set_preset(eq_ctxt, (int)prop[0]); } else { if (vsize < (2 + NUM_EQ_BANDS) * sizeof(int16_t)) { android_errorWriteLog(0x534e4554, "37563371"); ALOGE("\tERROR EQ_PARAM_PROPERTIES valueSize %d < %d", vsize, (2 + NUM_EQ_BANDS) * sizeof(int16_t)); p->status = -EINVAL; break; } if ((int)prop[1] != NUM_EQ_BANDS) { p->status = -EINVAL; break; } for (i = 0; i < NUM_EQ_BANDS; i++) { equalizer_set_band_level(eq_ctxt, i, (int)prop[2 + i]); } } } break; default: p->status = -EINVAL; break; } return 0; } int equalizer_set_device(effect_context_t *context, uint32_t device) { ALOGV("%s: device: %d", __func__, device); equalizer_context_t *eq_ctxt = (equalizer_context_t *)context; eq_ctxt->device = device; offload_eq_set_device(&(eq_ctxt->offload_eq), device); return 0; } int equalizer_reset(effect_context_t *context) { equalizer_context_t *eq_ctxt = (equalizer_context_t *)context; return 0; } int equalizer_init(effect_context_t *context) { ALOGV("%s", __func__); equalizer_context_t *eq_ctxt = (equalizer_context_t *)context; context->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; context->config.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; context->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT; context->config.inputCfg.samplingRate = 44100; context->config.inputCfg.bufferProvider.getBuffer = NULL; context->config.inputCfg.bufferProvider.releaseBuffer = NULL; context->config.inputCfg.bufferProvider.cookie = NULL; context->config.inputCfg.mask = EFFECT_CONFIG_ALL; context->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; context->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; context->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; context->config.outputCfg.samplingRate = 44100; context->config.outputCfg.bufferProvider.getBuffer = NULL; context->config.outputCfg.bufferProvider.releaseBuffer = NULL; context->config.outputCfg.bufferProvider.cookie = NULL; context->config.outputCfg.mask = EFFECT_CONFIG_ALL; set_config(context, &context->config); memset(&(eq_ctxt->offload_eq), 0, sizeof(struct eq_params)); offload_eq_set_preset(&(eq_ctxt->offload_eq), INVALID_PRESET); return 0; } int equalizer_enable(effect_context_t *context) { equalizer_context_t *eq_ctxt = (equalizer_context_t *)context; ALOGV("%s", __func__); if (!offload_eq_get_enable_flag(&(eq_ctxt->offload_eq))) { offload_eq_set_enable_flag(&(eq_ctxt->offload_eq), true); if (eq_ctxt->ctl) offload_eq_send_params(eq_ctxt->ctl, &eq_ctxt->offload_eq, OFFLOAD_SEND_EQ_ENABLE_FLAG | OFFLOAD_SEND_EQ_BANDS_LEVEL); } return 0; } int equalizer_disable(effect_context_t *context) { equalizer_context_t *eq_ctxt = (equalizer_context_t *)context; ALOGV("%s", __func__); if (offload_eq_get_enable_flag(&(eq_ctxt->offload_eq))) { offload_eq_set_enable_flag(&(eq_ctxt->offload_eq), false); if (eq_ctxt->ctl) offload_eq_send_params(eq_ctxt->ctl, &eq_ctxt->offload_eq, OFFLOAD_SEND_EQ_ENABLE_FLAG); } return 0; } int equalizer_start(effect_context_t *context, output_context_t *output) { equalizer_context_t *eq_ctxt = (equalizer_context_t *)context; ALOGV("%s: %p", __func__, output->ctl); eq_ctxt->ctl = output->ctl; if (offload_eq_get_enable_flag(&(eq_ctxt->offload_eq))) if (eq_ctxt->ctl) offload_eq_send_params(eq_ctxt->ctl, &eq_ctxt->offload_eq, OFFLOAD_SEND_EQ_ENABLE_FLAG | OFFLOAD_SEND_EQ_BANDS_LEVEL); return 0; } int equalizer_stop(effect_context_t *context, output_context_t *output __unused) { equalizer_context_t *eq_ctxt = (equalizer_context_t *)context; ALOGV("%s", __func__); eq_ctxt->ctl = NULL; return 0; }