/* * Copyright (C) 2025 Amlogic Corporation. * * 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. */ #include #include #include #include #include #include "aml_audio_mute.h" #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif #ifndef MAX #define MAX(a,b) ((a) > (b) ? (a) : (b)) #endif int aml_audio_mute_bytes_per_sample(mute_sample_format_t fmt) { switch (fmt) { case MUTE_FMT_S16: return 2; case MUTE_FMT_S32: return 4; case MUTE_FMT_FLOAT: return 4; default: return 2; } } int aml_audio_mute_init(aml_audio_mute_t *h, mute_sample_format_t fmt, int channels, int sample_rate, int default_fade_ms) { if (!h) return -1; h->format = fmt; h->channels = (channels > 0) ? channels : 2; h->sample_rate = (sample_rate > 0) ? sample_rate : 48000; /* compute default fade frames (ms -> frames) */ uint64_t frames = 0; if (default_fade_ms > 0) { frames = (uint64_t)((int64_t)default_fade_ms * h->sample_rate / 1000); if (frames == 0) frames = 1; } else { frames = 0; /* no fade if zero */ } h->default_fade_frames = frames; h->current_gain = 1.0f; h->target_gain = 1.0f; h->per_frame_delta = 0.0f; h->frames_remaining = 0; pthread_mutex_init(&h->config_mutex, NULL); return 0; } int aml_audio_mute_close(aml_audio_mute_t *h) { if (!h) return -1; pthread_mutex_destroy(&h->config_mutex); return 0; } int aml_audio_mute_config(aml_audio_mute_t *h, bool mute) { if (!h) return -1; float tgt = mute ? 0.0f : 1.0f; pthread_mutex_lock(&h->config_mutex); /* If already requested same target, return early */ if (h->target_gain == tgt) { pthread_mutex_unlock(&h->config_mutex); return 0; } /* read current gain */ float cur = h->current_gain; uint64_t frames = h->default_fade_frames; if (frames == 0) { /* immediate switch */ h->current_gain = tgt; h->target_gain = tgt; h->per_frame_delta = 0.0f; h->frames_remaining = 0; pthread_mutex_unlock(&h->config_mutex); return 0; } /* If a fade is in progress and this is a reverse request, use elapsed frames as new duration for more symmetric reversal */ if (h->frames_remaining > 0 && h->target_gain != tgt) { uint64_t elapsed = 0; if (h->default_fade_frames > h->frames_remaining) elapsed = h->default_fade_frames - h->frames_remaining; if (elapsed > 0) frames = elapsed; /* Use elapsed frames */ /* Fall back to default frames when elapsed == 0 */ } /* start fade from current value to target over default frames */ float delta = (tgt - cur) / (float)frames; h->target_gain = tgt; h->per_frame_delta = delta; h->frames_remaining = frames; pthread_mutex_unlock(&h->config_mutex); return 0; } /* small helpers to apply ramp or constant gain */ static void apply_ramp_float(float *samples, size_t frames, int ch, float start_gain, float delta) { for (size_t f = 0; f < frames; ++f) { float g = start_gain + delta * (float)f; size_t base = f * (size_t)ch; for (int c = 0; c < ch; ++c) { samples[base + c] *= g; } } } static void apply_ramp_s32(int32_t *samples, size_t frames, int ch, float start_gain, float delta) { for (size_t f = 0; f < frames; ++f) { float g = start_gain + delta * (float)f; size_t base = f * (size_t)ch; for (int c = 0; c < ch; ++c) { samples[base + c] *= g; } } } static void apply_ramp_s16(int16_t *samples, size_t frames, int ch, float start_gain, float delta) { for (size_t f = 0; f < frames; ++f) { float g = start_gain + delta * (float)f; size_t base = f * (size_t)ch; for (int c = 0; c < ch; ++c) { samples[base + c] *= g; } } } int aml_audio_mute_process(aml_audio_mute_t *h, void *in_data, size_t size) { if (!h || !in_data || size == 0) return -1; int bps = aml_audio_mute_bytes_per_sample(h->format); if (bps <= 0 || h->channels <= 0) return -1; size_t nframes = size / (bps * (size_t)h->channels); if (nframes == 0) return 0; /* Lock for full processing (simple design per request) */ pthread_mutex_lock(&h->config_mutex); float cur = h->current_gain; uint64_t rem = h->frames_remaining; float delta = h->per_frame_delta; float tgt = h->target_gain; /* Fast paths when no fade pending */ if (rem == 0) { if (cur <= 0.000001f) { memset(in_data, 0, size); pthread_mutex_unlock(&h->config_mutex); return 0; } if (cur >= 0.999999f) { pthread_mutex_unlock(&h->config_mutex); return 0; /* bypass */ } /* rem==0 but cur in (0,1): treat as immediate to target */ h->current_gain = tgt; if (tgt <= 0.000001f) memset(in_data, 0, size); pthread_mutex_unlock(&h->config_mutex); return 0; } /* There is a fade in progress. Process up to rem frames as ramp, rest with target gain. Use at most two passes for performance. */ size_t frames_total = nframes; size_t partA = (size_t)MIN((uint64_t)frames_total, rem); size_t partB = frames_total - partA; /* Apply ramp on first partA frames */ if (partA > 0) { if (h->format == MUTE_FMT_FLOAT) { apply_ramp_float((float*)in_data, partA, h->channels, cur, delta); } else if (h->format == MUTE_FMT_S32) { apply_ramp_s32((int32_t*)in_data, partA, h->channels, cur, delta); } else if (h->format == MUTE_FMT_S16) { apply_ramp_s16((int16_t*)in_data, partA, h->channels, cur, delta); } else { pthread_mutex_unlock(&h->config_mutex); return -1; } /* update current_gain and frames_remaining */ float new_cur = cur + delta * (float)partA; h->current_gain = new_cur; if (h->frames_remaining > partA) h->frames_remaining -= partA; else h->frames_remaining = 0; cur = new_cur; rem = h->frames_remaining; } /* Handle remaining frames (partB) with final target gain */ if (partB > 0) { void *ptr = (uint8_t*)in_data + partA * (size_t)bps * (size_t)h->channels; size_t bytesB = partB * (size_t)bps * (size_t)h->channels; if (tgt <= 0.000001f) { memset(ptr, 0, bytesB); } else { /* bypass */ } /* If we consumed the remaining fade frames, clear them */ if (rem <= partB) { h->frames_remaining = 0; h->per_frame_delta = 0.0f; h->current_gain = tgt; } else { h->frames_remaining -= partB; h->current_gain = cur + delta * (float)partB; } } pthread_mutex_unlock(&h->config_mutex); return 0; }