通过麦克风录音的方式实现简单的幻彩灯音乐律动效果

API

#define MAX_H	(360)//色调最大值
#define MAX_S	(255)//饱和度最大值
#define MAX_V	(200)//明度最大值

//HSV
typedef struct _USER_HSV_COLOUR_{
   volatile int h;//色调(H)0°~360°
   volatile int s;//饱和度(S)0~255
   volatile int v;//明度(V)0~255
}HSV_COLOUR;

//RGB
typedef struct _RGB_COLOUR_{
    u8 b;//蓝
    u8 r;//红
    u8 g;//绿
}RGB_COLOUR;
RGB_COLOUR hsv2rgb(HSV_COLOUR hsv);
HSV_COLOUR rgb2hsv(RGB_COLOUR rgb);

//数据发送控制
//0 清除cnt计数 不发送数据
//其他发送数据次数
void rgb_send_cnt(u8 cnt);

//设置第几颗rgb颜色
/*
priv:rgb 配置数据
colour:rgb 颜色
number:离io口最近的那颗灯为第一颗,最后一颗可以表示为 -1或者(USER_RGB_NUMBER-1)
*/
void rgb_colour_only_set(RGB_COLOUR* colour, s32 number) ;

实现

double mic_db = 0; 
#define PCM_SAMPLE_MAX 32767.0   // PCM 样本的最大值
#define REFERENCE_DB 1.0   // 参考值(1.0 表示最大音量)

//16k 16bit 单声道
double get_pcm_energ(short* pcm_data, int pcm_data_length)
{
    double rms = 0.0;

    // 计算 PCM 数据的均方根值
    for (int i = 0; i < pcm_data_length; i++) {
        double sample = (double)pcm_data[i] / PCM_SAMPLE_MAX;
        rms += sample * sample;
    }
    rms = sqrt(rms / pcm_data_length);

    // 计算 mic_db 值
    double mic_db = 20 * log10(rms / REFERENCE_DB);
    return mic_db;
}

// 生成一个伪随机数
int pseudo_random()
{
    static int seed = 0;
    seed = ((seed * 214013 + 2531011) >> 16) & 0x7FFF;
    return seed;
}

//根据 get_pcm_energ 计算音频数据长度,中断中调用
void mode_music_push()
{
#define SMOOTH_FACTOR 0.8   // 平滑因子 
#define COLOR_CHANGE_INTERVAL 24   // 颜色变化间隔(假设为100个计数单位)
#define MAX_CONSECUTIVE_LIGHTUP (RGB_NUMBER - 1) // 连续点亮灯珠的最大次数
#define SUBSEQUENT_LIGHTUP_COUNT 5 // 后续点亮的灯珠数量

    static double smoothed_db = -30.0;
    static int count = 0;
    static unsigned short  hsv_count = 0;
    static int consecutive_lightup_count = 0; // 连续点亮灯珠的次数
    static int subsequent_lightup_index = 0; // 后续点亮灯珠的索引
    static int subsequent_lightup_cur_index = 0; // 后续点亮灯珠的索引
    RGB_COLOUR tp;
    HSV_COLOUR temp_hsv = { .h = 0, .s = MAX_S, .v = MAX_V };

    // 使用滑动平均值平滑 mic_db 的变化
    smoothed_db = (SMOOTH_FACTOR * mic_db) + ((1 - SMOOTH_FACTOR) * smoothed_db);
    if (smoothed_db < -30) smoothed_db = -30;
    if (smoothed_db > -5) smoothed_db = -5;

    // 计算需要点亮的灯珠数量
    int num_leds = (int)(((smoothed_db - (-30.0)) / ((-5.0) - (-30.0))) * (RGB_NUMBER - 1));

    // 计算每组需要点亮的灯珠数量
    int num_leds_per_group = (num_leds + 1) / 2;

    // 计算起始点亮的灯珠索引
    int start_index = 11;
    int end_index = 12;

    // 每次执行增加计数器的值
    count++;

    // 检查是否到了改变颜色的时间间隔
    if (count >= COLOR_CHANGE_INTERVAL) {
        // 随机生成新的颜色
        srand(pseudo_random());
        // hsv_count = rand() % 360;
        hsv_count = (hsv_count + 90) % 360;  // 将hsv_count分成4份,每一份为90度
        // 重置计数器
        // printf("%d ", hsv_count);
        count = 0;
    }

    // 判断连续点亮灯珠的次数
    if (num_leds >= MAX_CONSECUTIVE_LIGHTUP) {
        consecutive_lightup_count++;

        if (consecutive_lightup_count == 1) {
            srand(pseudo_random());
            subsequent_lightup_cur_index = rand() % 5 + 1; // 生成1-5之间的随机数
            subsequent_lightup_index = 0;
        }

    } else {
        consecutive_lightup_count = 0;
        subsequent_lightup_index = 0; // 重置后续点亮的索引
    }

    // 当连续点亮灯珠的次数达到最大值时,减少点亮的灯珠数量
    if (consecutive_lightup_count >= SUBSEQUENT_LIGHTUP_COUNT) {
        num_leds_per_group = (RGB_NUMBER / 2 - 1) - subsequent_lightup_index - 1;
        subsequent_lightup_index++;

        // 判断是否达到后续点亮的次数上限
        if (subsequent_lightup_index > subsequent_lightup_cur_index) {
            consecutive_lightup_count = 0; // 重置连续点亮灯珠的次数
            subsequent_lightup_index = 0; // 重置后续点亮的索引
        }
    }

    // 向两头逐渐点亮灯珠
    for (int i = 0; i < num_leds_per_group; i++) {
        // 点亮第一组灯珠
        if (start_index - i >= 0) {
            // 设置为需要点亮的亮度或颜色
            temp_hsv.h = (hsv_count + (i * 90 / num_leds_per_group)) % 360; // 将temp_hsv.h分成4份,每一份为90度
            tp = hsv2rgb(temp_hsv);
            rgb_colour_only_set(&tp, start_index - i);
        }

        // 点亮第二组灯珠
        if (end_index + i < RGB_NUMBER) {
            // 设置为需要点亮的亮度或颜色 
            temp_hsv.h = (hsv_count + (i * 90 / num_leds_per_group)) % 360; // 将temp_hsv.h分成4份,每一份为90度
            tp = hsv2rgb(temp_hsv);
            rgb_colour_only_set(&tp, end_index + i);
        }
    }

    // 关闭未点亮的灯珠
    for (int i = num_leds_per_group; i <= start_index; i++) {
        // 设置为不需要点亮的亮度或颜色
        temp_hsv.v = 0;
        tp = hsv2rgb(temp_hsv);
        rgb_colour_only_set(&tp, start_index - i);
    }
    for (int i = end_index + num_leds_per_group; i < RGB_NUMBER; i++) {
        // 设置为不需要点亮的亮度或颜色 
        temp_hsv.v = 0;
        tp = hsv2rgb(temp_hsv);
        rgb_colour_only_set(&tp, i);
    }

    //发送一次数据
    rgb_send_cnt(1);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值