forked from PAWPAW/lib_rgb
新增:从samples读取响度,转化为灯效
- 版本更新至0.0.4 - 添加了缓存,以避免并行导致的数据冲突 - 重构了文件结构 - 增加了从sample提取响度,并转化为RMS的接口 - 增加了从RMS转换为dB的接口
This commit is contained in:
@@ -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() // 定义主函数
|
||||
|
||||
@@ -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() // 定义主函数
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define MICRO_SECOND 100U // 微秒
|
||||
|
||||
/**
|
||||
* 反转缓冲区中的元素。
|
||||
*
|
||||
|
||||
@@ -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灯的数量
|
||||
|
||||
55
lib_rgb/api/samples_to_levels.h
Normal file
55
lib_rgb/api/samples_to_levels.h
Normal 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
|
||||
44
lib_rgb/api/visualize_volume.h
Normal file
44
lib_rgb/api/visualize_volume.h
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
117
lib_rgb/src/samples_to_levels.c
Normal file
117
lib_rgb/src/samples_to_levels.c
Normal 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]));
|
||||
}
|
||||
}
|
||||
65
lib_rgb/src/visualize_volume.c
Normal file
65
lib_rgb/src/visualize_volume.c
Normal 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(¤t_hue);
|
||||
}
|
||||
}
|
||||
@@ -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]));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user