新增:从samples读取响度,转化为灯效

- 版本更新至0.0.4
- 添加了缓存,以避免并行导致的数据冲突
- 重构了文件结构
- 增加了从sample提取响度,并转化为RMS的接口
- 增加了从RMS转换为dB的接口
This commit is contained in:
2023-11-24 17:59:03 +08:00
parent a595a7825c
commit 5024b92298
12 changed files with 290 additions and 85 deletions

View File

@@ -8,7 +8,7 @@
#include "stdint.h"
#include <stdio.h> // 包含基本的输入输出函数
#include <platform.h> // 包含对封装的定义,引用以使用 on tile[] 语法
#include "volume_level.h"
#include "samples_to_levels.h"
#include "rgb_effect.h"
int main() // 定义主函数

View File

@@ -8,7 +8,7 @@
#include "stdint.h"
#include <stdio.h> // 包含基本的输入输出函数
#include <platform.h> // 包含对封装的定义,引用以使用 on tile[] 语法
#include "volume_level.h"
#include "samples_to_levels.h"
#include "rgb_effect.h"
int main() // 定义主函数

View File

@@ -3,6 +3,8 @@
#include <stdint.h>
#include <stddef.h>
#define MICRO_SECOND 100U // 微秒
/**
* 反转缓冲区中的元素。
*

View File

@@ -4,7 +4,7 @@
#include <stdint.h>
#include <stddef.h>
#include "rgb_driver.h"
#include "volume_level.h"
#include "samples_to_levels.h"
#include "misc_utils.h"
// RGB灯的数量

View File

@@ -0,0 +1,55 @@
#ifndef VOLUME_LEVEL_H
#define VOLUME_LEVEL_H
#include <stdint.h>
#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

View File

@@ -0,0 +1,44 @@
#ifndef CACHE_OPERATIONS_H
#define CACHE_OPERATIONS_H
#include <stdint.h>
#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

View File

@@ -1,24 +0,0 @@
#ifndef VOLUME_LEVEL_H
#define VOLUME_LEVEL_H
#include <stdint.h>
/**
* 根据响度值获取音量等级。
*
* @param loudness_value 当前的响度值。
* @return 对应的音量等级。
*
* 此函数通过比较响度值与预定义的音量等级表中的响度阈值,来确定当前的音量等级。
* 它从最大响度阈值开始比较,找到第一个小于或等于当前响度值的阈值,返回对应的音量等级。
*/
unsigned get_volume_level(int loudness_value);
/**
* 测试不同响度值对应的音量等级。
*
* 此函数定义了一系列响度值,并使用 get_volume_level 函数
* 来获取每个响度值对应的音量等级,然后打印出来。
*/
void volume_level_test();
#endif //VOLUME_LEVEL_H

View File

@@ -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

View File

@@ -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)
{

View File

@@ -0,0 +1,117 @@
#include <stdint.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#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]));
}
}

View File

@@ -0,0 +1,65 @@
#include <stdio.h>
#include <stddef.h>
#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(&current_hue);
}
}

View File

@@ -1,49 +0,0 @@
#include <stdint.h>
#include <stdio.h>
#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]));
}
}