Files
2026-08-24 10:03:24 +08:00

244 lines
7.4 KiB
C

/*
* 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 <string.h>
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <limits.h>
#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;
}