From 5024b92298b479ae0e22a2b55de66d9af5337786 Mon Sep 17 00:00:00 2001 From: Vergil Wong Date: Fri, 24 Nov 2023 17:59:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E4=BB=8Esamples?= =?UTF-8?q?=E8=AF=BB=E5=8F=96=E5=93=8D=E5=BA=A6=EF=BC=8C=E8=BD=AC=E5=8C=96?= =?UTF-8?q?=E4=B8=BA=E7=81=AF=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 版本更新至0.0.4 - 添加了缓存,以避免并行导致的数据冲突 - 重构了文件结构 - 增加了从sample提取响度,并转化为RMS的接口 - 增加了从RMS转换为dB的接口 --- .../src/main.xc | 2 +- .../src/main.xc | 2 +- lib_rgb/api/misc_utils.h | 2 + lib_rgb/api/rgb_effect.h | 2 +- lib_rgb/api/samples_to_levels.h | 55 ++++++++ lib_rgb/api/visualize_volume.h | 44 +++++++ lib_rgb/api/volume_level.h | 24 ---- lib_rgb/module_build_info | 7 +- lib_rgb/src/rgb_effect.xc | 6 +- lib_rgb/src/samples_to_levels.c | 117 ++++++++++++++++++ lib_rgb/src/visualize_volume.c | 65 ++++++++++ lib_rgb/src/volume_level.xc | 49 -------- 12 files changed, 290 insertions(+), 85 deletions(-) create mode 100644 lib_rgb/api/samples_to_levels.h create mode 100644 lib_rgb/api/visualize_volume.h delete mode 100644 lib_rgb/api/volume_level.h create mode 100644 lib_rgb/src/samples_to_levels.c create mode 100644 lib_rgb/src/visualize_volume.c delete mode 100644 lib_rgb/src/volume_level.xc diff --git a/examples/app_test_cycleHSV_with_vol_level_example/src/main.xc b/examples/app_test_cycleHSV_with_vol_level_example/src/main.xc index 9c593f9..b5af503 100644 --- a/examples/app_test_cycleHSV_with_vol_level_example/src/main.xc +++ b/examples/app_test_cycleHSV_with_vol_level_example/src/main.xc @@ -8,7 +8,7 @@ #include "stdint.h" #include // 包含基本的输入输出函数 #include // 包含对封装的定义,引用以使用 on tile[] 语法 -#include "volume_level.h" +#include "samples_to_levels.h" #include "rgb_effect.h" int main() // 定义主函数 diff --git a/examples/app_test_fill_gradient_with_groups/src/main.xc b/examples/app_test_fill_gradient_with_groups/src/main.xc index e874bc3..bc53c75 100644 --- a/examples/app_test_fill_gradient_with_groups/src/main.xc +++ b/examples/app_test_fill_gradient_with_groups/src/main.xc @@ -8,7 +8,7 @@ #include "stdint.h" #include // 包含基本的输入输出函数 #include // 包含对封装的定义,引用以使用 on tile[] 语法 -#include "volume_level.h" +#include "samples_to_levels.h" #include "rgb_effect.h" int main() // 定义主函数 diff --git a/lib_rgb/api/misc_utils.h b/lib_rgb/api/misc_utils.h index 8cb84dd..8af0251 100644 --- a/lib_rgb/api/misc_utils.h +++ b/lib_rgb/api/misc_utils.h @@ -3,6 +3,8 @@ #include #include +#define MICRO_SECOND 100U // 微秒 + /** * 反转缓冲区中的元素。 * diff --git a/lib_rgb/api/rgb_effect.h b/lib_rgb/api/rgb_effect.h index 07f6e42..44a6d1a 100644 --- a/lib_rgb/api/rgb_effect.h +++ b/lib_rgb/api/rgb_effect.h @@ -4,7 +4,7 @@ #include #include #include "rgb_driver.h" -#include "volume_level.h" +#include "samples_to_levels.h" #include "misc_utils.h" // RGB灯的数量 diff --git a/lib_rgb/api/samples_to_levels.h b/lib_rgb/api/samples_to_levels.h new file mode 100644 index 0000000..f5cefa8 --- /dev/null +++ b/lib_rgb/api/samples_to_levels.h @@ -0,0 +1,55 @@ +#ifndef VOLUME_LEVEL_H +#define VOLUME_LEVEL_H +#include + +#define CHANNEL_CNT NUM_RGB_GROUPS // 通道数应与RGB组数一致 + +#define FRAME_CNT 64 // 每个通道的帧数 + +#define FULLSCALE 32767.0f // 对于16位音频的全幅值 + +#define CACHE_SIZE FRAME_CNT * CHANNEL_CNT // 定义缓存大小 + +#define INVALID_DATA -1 // 无效数据 + +#define VOL_LEVEL_TABLE \ + { 0 , 6 }, \ + { -3 , 5 }, \ + { -9 , 4 }, \ + { -14, 3 }, \ + { -30, 2 }, \ + { -62, 1 }, + +/** + * 根据响度值获取音量等级。 + * + * @param loudness_value 当前的响度值。 + * @return 对应的音量等级。 + * + * 此函数通过比较响度值与预定义的音量等级表中的响度阈值,来确定当前的音量等级。 + * 它从最大响度阈值开始比较,找到第一个小于或等于当前响度值的阈值,返回对应的音量等级。 + */ +size_t get_volume_level(int loudness_value); + +/** + * 测试不同响度值对应的音量等级。 + * + * 此函数定义了一系列响度值,并使用 get_volume_level 函数 + * 来获取每个响度值对应的音量等级,然后打印出来。 + */ +void volume_level_test(); + +/** + * 将音频样本转换为音量级别。 + * + * @param sample_in 指向输入样本数组的指针。 + * @param levels 指向输出音量级别数组的指针。 + * + * 此函数通过计算输入样本的均方根(RMS)值或峰值来确定音量级别。 + * 它首先将输入样本复制到临时数组以避免在处理过程中被修改。 + * 然后,对于每个RGB组,函数计算样本的RMS或峰值,并将其转换为音量级别。 + * 最后,这些级别将被用于驱动RGB效果的可视化表现。 + */ +void samples_to_levels(int16_t sample_in[], size_t levels[]); + +#endif //VOLUME_LEVEL_H \ No newline at end of file diff --git a/lib_rgb/api/visualize_volume.h b/lib_rgb/api/visualize_volume.h new file mode 100644 index 0000000..46d59b5 --- /dev/null +++ b/lib_rgb/api/visualize_volume.h @@ -0,0 +1,44 @@ +#ifndef CACHE_OPERATIONS_H +#define CACHE_OPERATIONS_H +#include +#include "samples_to_levels.h" +#include "rgb_effect.h" +/** + * \brief 初始化缓存缓冲区。 + * + * 此函数将两个缓存缓冲区中的所有数据设置为INVALID_DATA。 + */ +void initialize_caches(); + +/** + * \brief 交换活动和非活动缓存。 + * + * 此函数交换活动缓存和非活动缓存的指针,使得当前的非活动缓存成为活动缓存,反之亦然。 + */ +void swap_caches(); + +/** + * \brief 向非活动缓存写入数据。 + * + * 此函数将数据写入到非活动缓存的指定地址。 + * 如果地址无效,则不执行任何操作。 + * + * \param address 要写入数据的地址。 + * \param data 要写入的数据。 + */ +void write_to_inactive_cache(int address, int16_t data); + +/** + * 根据音频响度点亮不同数量的RGB灯。 + * + * 此函数首先计算当前音频样本的音量级别。 + * 然后,根据这些级别,使用当前的渐变颜色填充RGB数组。 + * 最后,更新当前颜色以在下一次迭代中使用。 + * + * 函数使用全局变量active_cache作为音频样本输入, + * current_color作为每组RGB灯的当前颜色, + * current_hue作为当前色调值,用于生成下一个颜色。 + */ +void visualize_loudness_driver(); +#endif // CACHE_OPERATIONS_H +// TODO: Refact this shit \ No newline at end of file diff --git a/lib_rgb/api/volume_level.h b/lib_rgb/api/volume_level.h deleted file mode 100644 index 7c7e1e8..0000000 --- a/lib_rgb/api/volume_level.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef VOLUME_LEVEL_H -#define VOLUME_LEVEL_H -#include - -/** - * 根据响度值获取音量等级。 - * - * @param loudness_value 当前的响度值。 - * @return 对应的音量等级。 - * - * 此函数通过比较响度值与预定义的音量等级表中的响度阈值,来确定当前的音量等级。 - * 它从最大响度阈值开始比较,找到第一个小于或等于当前响度值的阈值,返回对应的音量等级。 - */ -unsigned get_volume_level(int loudness_value); - -/** - * 测试不同响度值对应的音量等级。 - * - * 此函数定义了一系列响度值,并使用 get_volume_level 函数 - * 来获取每个响度值对应的音量等级,然后打印出来。 - */ -void volume_level_test(); - -#endif //VOLUME_LEVEL_H \ No newline at end of file diff --git a/lib_rgb/module_build_info b/lib_rgb/module_build_info index bab64de..e41f7c8 100644 --- a/lib_rgb/module_build_info +++ b/lib_rgb/module_build_info @@ -1,10 +1,9 @@ -VERSION = 0.0.3 +VERSION = 0.0.4 -DEPENDENT_MODULES = +DEPENDENT_MODULES = lib_xcore_math(>=2.1.0) \ -MODULE_XCC_FLAGS = $(XCC_FLAGS) -g -O3 -Wall -Wextra -Werror +MODULE_XCC_FLAGS = $(XCC_FLAGS) -g -O3 -Wall EXPORT_INCLUDE_DIRS = api SOURCE_DIRS = src - diff --git a/lib_rgb/src/rgb_effect.xc b/lib_rgb/src/rgb_effect.xc index cd8dd17..df01ac3 100644 --- a/lib_rgb/src/rgb_effect.xc +++ b/lib_rgb/src/rgb_effect.xc @@ -5,14 +5,10 @@ #include "xclib.h" #include "timer.h" #include "rgb_driver.h" -#include "volume_level.h" +#include "samples_to_levels.h" #include "misc_utils.h" #include "rgb_effect.h" -// TODO:当RGB_MAX等值超限时,raise error -// 定义一个枚举来表示渐变方向 - - // 检查HSV值是否在有效范围内,如果溢出则将值设为最大值,并打印报错 void is_HSV_valid(uint32_t *hue, uint32_t *sat, uint32_t *value) { diff --git a/lib_rgb/src/samples_to_levels.c b/lib_rgb/src/samples_to_levels.c new file mode 100644 index 0000000..aeec7e2 --- /dev/null +++ b/lib_rgb/src/samples_to_levels.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include "rgb_effect.h" +#include "xmath/xmath.h" + +// 定义音量等级结构体 +typedef struct { + int vol; // 响度值 + unsigned level; // 音量等级 +} vol_level_table_t; + +const vol_level_table_t vol_level_table[] = {VOL_LEVEL_TABLE}; +#define VOLUME_LEVELS_COUNT (sizeof(vol_level_table) / sizeof(vol_level_table[0])) + +// 根据响度值获取音量等级 +size_t get_volume_level(int loudness_value) { + // 默认返回音量等级0 + int level = 0; + + // 从最大响度值开始向下找 + for (size_t i = 0; i < VOLUME_LEVELS_COUNT; ++i) { + if (loudness_value >= vol_level_table[i].vol) { + level = vol_level_table[i].level; + break; + } + } + + return level; +} + +// 计算一段音频样本的RMS电平,此处采用均值算法 +int compute_rms_avg(int16_t *samples, size_t numSamples) +{ + float sum = 0.0; + + // 累加所有样本的平方 + for (size_t i = 0; i < numSamples; ++i) + { + sum += samples[i] * samples[i]; + } + + // 计算平均值 + float mean = sum / numSamples; + + // 计算平均值的平方根,得到RMS值 + float rms = sqrtf(mean); + + // 如果RMS值为0,则返回负无穷大分贝值 + if (rms == 0.0f) + { + return -96; // 或者定义一个表示静音的常量 + } + else + { + // 计算相对于满刻度的分贝值 + int rms_dB = 20.0f * log10(rms / FULLSCALE); + return rms_dB; + } +} + +// 计算一段音频样本的RMS电平,峰值算法 +int compute_rms_peak(int16_t *samples, size_t numSamples) +{ + float max_sample = 0.0; + + // 累加所有样本的平方 + for (size_t i = 0; i < numSamples; ++i) + { + if (abs(samples[i]) > max_sample) + max_sample = abs(samples[i]); + } + + // 如果RMS值为0,则返回负无穷大分贝值 + if (max_sample == 0.0f) + { + return -96; // 或者定义一个表示静音的常量 + } + else + { + // 计算相对于满刻度的分贝值 + int rms_dB = 20.0f * log10(max_sample / FULLSCALE); + return rms_dB; + } +} + +// 通过样本能量计算levels +void samples_to_levels(int16_t sample_in[], size_t levels[]) +{ + int16_t sample_in_tmp[CACHE_SIZE]; + // 防止产生内存抖动,处理时,sample有可能被写入 + xs3_memcpy(sample_in_tmp, sample_in, sizeof(sample_in_tmp)); + size_t rms; + for (size_t i = 0; i < NUM_RGB_GROUPS; i++) + { + // 根据样本计算rms dB, 可以使用不同的算法(均值/峰值) + // rms = compute_rms_avg(sample_in + i * FRAME_CNT, FRAME_CNT); + rms = compute_rms_peak(sample_in_tmp + i * FRAME_CNT, FRAME_CNT); + // 从rms dB转换为音量level + levels[i] = get_volume_level(rms); + // printf("rms[%d]: %d \n", i, rms); + } +} + +// 测试不同响度值对应的音量等级是否符合预期 +void volume_level_test() { + // 测试不同的响度值 + uint32_t test_loudness_values[] = {1, 0, -2, -5, -8, -20, -30, -40, -60, -62, -70, }; + int num_tests = sizeof(test_loudness_values) / sizeof(test_loudness_values[0]); + + for (int i = 0; i < num_tests; ++i) { + printf("Loudness: %lu dB, Volume Level: %u\n", + test_loudness_values[i], + get_volume_level((size_t)test_loudness_values[i])); + } +} \ No newline at end of file diff --git a/lib_rgb/src/visualize_volume.c b/lib_rgb/src/visualize_volume.c new file mode 100644 index 0000000..6bc8865 --- /dev/null +++ b/lib_rgb/src/visualize_volume.c @@ -0,0 +1,65 @@ +#include +#include +#include "rgb_effect.h" + +// 声明两个缓存缓冲区 +int16_t cache1[CACHE_SIZE]; +int16_t cache2[CACHE_SIZE]; + +// 变量跟踪活动缓存和非活动缓存 +int16_t *active_cache = cache1; +int16_t *inactive_cache = cache2; + +// 初始化缓存 +void initialize_caches() +{ + for (int i = 0; i < CACHE_SIZE; ++i) + { + cache1[i] = INVALID_DATA; + cache2[i] = INVALID_DATA; + } +} + +// 交换活动和非活动缓存 +void swap_caches() +{ + int16_t *temp = active_cache; + active_cache = inactive_cache; + inactive_cache = temp; +} + +// 向非活动缓存写入数据 +void write_to_inactive_cache(int address, int16_t data) +{ + if (address < 0 || address >= CACHE_SIZE) + { + printf("[x] fuck\n"); + return; // 地址越界 + } + inactive_cache[address] = data; + // printf("inactive_cache: %d\n", inactive_cache[address]); +} + +uint32_t buf[NUM_RGBS]; // 定义一个用于存储RGB值的缓冲区,大小由NUM_RGBS宏确定 +uint32_t current_hue = 0; // 从红色开始的当前色相值 +uint32_t current_color[NUM_RGB_GROUPS] = {0x000000, 0x000000}; + + +// 根据音频的响度,点亮不同数量的rgb灯 +void visualize_loudness_driver() +{ + // 响度对应的RGB灯等级 + size_t levels[NUM_RGB_GROUPS]; + + // 通过样本能量计算levels + samples_to_levels(active_cache, levels); + + // 用当前渐变颜色填充RGB数组,然后发送给rgb阵列 + fill_gradient_with_groups(buf, current_color, levels); + + // 更改下一个颜色 + for (size_t i = 0; i < NUM_RGB_GROUPS; i++) + { + *(current_color + i) = cycleHSV(¤t_hue); + } +} \ No newline at end of file diff --git a/lib_rgb/src/volume_level.xc b/lib_rgb/src/volume_level.xc deleted file mode 100644 index 683eebd..0000000 --- a/lib_rgb/src/volume_level.xc +++ /dev/null @@ -1,49 +0,0 @@ -#include -#include - - -#define VOL_LEVEL_TABLE \ - { 0 , 6 }, \ - { -3 , 5 }, \ - { -9 , 4 }, \ - { -14, 3 }, \ - { -30, 2 }, \ - { -62, 1 }, - -// 定义音量等级结构体 -typedef struct { - int vol; // 响度值 - unsigned level; // 音量等级 -} vol_level_table_t; - -const vol_level_table_t vol_level_table[] = {VOL_LEVEL_TABLE}; -#define VOLUME_LEVELS_COUNT (sizeof(vol_level_table) / sizeof(vol_level_table[0])) - -// 根据响度值获取音量等级 -unsigned get_volume_level(int loudness_value) { - // 默认返回音量等级0 - int level = 0; - - // 从最大响度值开始向下找 - for (size_t i = 0; i < VOLUME_LEVELS_COUNT; ++i) { - if (loudness_value >= vol_level_table[i].vol) { - level = vol_level_table[i].level; - break; // 找到后立即退出循环 - } - } - - return level; -} - -// 测试不同响度值对应的音量等级是否符合预期 -void volume_level_test() { - // 测试不同的响度值 - uint32_t test_loudness_values[] = {1, 0, -2, -5, -8, -20, -30, -40, -60, -62, -70, }; - int num_tests = sizeof(test_loudness_values) / sizeof(test_loudness_values[0]); - - for (int i = 0; i < num_tests; ++i) { - printf("Loudness: %u dB, Volume Level: %u\n", - test_loudness_values[i], - get_volume_level((int)test_loudness_values[i])); - } -} \ No newline at end of file