初始提交卡包 USB 工程

纳入固件源码、屏幕组件、nand_xfer 工具和文档。忽略本机构建产物、sdkconfig,以及含本机路径的依赖锁。

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
zhengxin
2026-10-13 21:54:50 +08:00
co-authored by Cursor
commit 851e347cf5
260 changed files with 269349 additions and 0 deletions
+379
View File
@@ -0,0 +1,379 @@
#include <stdint.h>
#include "esp_log.h"
#include "NS4168/micro_decoder_wrapper.h"
#include "NS4168/wav_demux.h"
#include "NS4168/ns4168.h"
#include <math.h>
#include <stdio.h>
#include <stdint.h>
static FILE* g_fp = NULL;
void md_init(const char* path)
{
g_fp = fopen(path, "rb"); // 标准 C
}
int md_read(uint8_t* buf, int len)
{
if (!g_fp) return 0;
return fread(buf, 1, len, g_fp); // 标准 C
}
void md_deinit()
{
if (g_fp) {
fclose(g_fp); // 标准 C
g_fp = NULL; // 标准 C(替换 nullptr)
}
}
void play_wav(void)
{
ns4168_i2s_init();
uint8_t header[70];
size_t written = 0;
int ret;
md_init("/extflash/ring2.wav");
md_read(header, sizeof(header));
ESP_LOG_BUFFER_HEX("WAV", header, sizeof(header));
ESP_LOGI("WAV", "WAV解析成功,开始播放");
uint8_t buf[1024];
int n;
while ((n = md_read(buf, sizeof(buf))) > 0) {
i2s_write(NS4168_I2S_PORT, buf, n, &written, portMAX_DELAY);
}
md_deinit();
ESP_LOGI("WAV", "WAV播放结束");
}
/**
* @param volume 音量系数,0.0=静音,1.0=原始音量,推荐 0.0~1.0
*/
void play_wav2(float volume)
{
uint8_t header[70];
size_t written = 0;
md_init("/extflash/ring2.wav");
md_read(header, sizeof(header));
ESP_LOG_BUFFER_HEX("WAV", header, sizeof(header));
ESP_LOGI("WAV", "WAV解析成功,开始播放");
uint8_t buf[1024];
int n;
while ((n = md_read(buf, sizeof(buf))) > 0) {
// 仅当音量不是 1.0 时才缩放(优化性能)
if (volume != 1.0f) {
int16_t *samples = (int16_t *)buf;
int num_samples = n / 2; // 假定为 16-bit PCM
for (int i = 0; i < num_samples; i++) {
int32_t val = (int32_t)(samples[i] * volume);
// 饱和限幅,防止溢出破音
if (val > 32767) val = 32767;
if (val < -32768) val = -32768;
samples[i] = (int16_t)val;
}
}
i2s_write(NS4168_I2S_PORT, buf, n, &written, portMAX_DELAY);
}
md_deinit();
ESP_LOGI("WAV", "WAV播放结束");
}
void play_wav3(float volume)
{
// 1. 打开文件
md_init("/extflash/ring2.wav");
// 2. 读取头部(沿用 70 字节,兼容你的原逻辑)
uint8_t header[70];
md_read(header, sizeof(header));
ESP_LOG_BUFFER_HEX("WAV", header, sizeof(header));
ESP_LOGI("WAV", "WAV解析成功,开始播放,音量=%.2f", volume);
// 3. 将浮点音量转换为 Q15 定点数 (范围 0 ~ 32768)
int32_t vol_fixed = (int32_t)(volume * 32768.0f + 0.5f);
if (vol_fixed > 32768) vol_fixed = 32768;
if (vol_fixed < 0) vol_fixed = 0;
size_t written = 0;
uint8_t buf[1024];
int n;
// 4. 循环读取音频数据并处理
while ((n = md_read(buf, sizeof(buf))) > 0) {
// 确保处理偶数个字节,防止破坏样本对齐
int bytes_to_process = (n / 2) * 2;
int num_samples = bytes_to_process / 2;
int16_t *samples = (int16_t *)buf;
// 音量接近静音时直接填零(避免处理微小信号产生的噪声)
if (vol_fixed < 32) {
memset(buf, 0, bytes_to_process);
}
else if (vol_fixed != 32768) { // 不等于 1.0 时才缩放
for (int i = 0; i < num_samples; i++) {
// 整数定点缩放 + 四舍五入
int32_t val = ((int32_t)samples[i] * vol_fixed + 16384) >> 15;
// 饱和限幅(实际不会溢出,但保留保护)
if (val > 32767) val = 32767;
if (val < -32768) val = -32768;
samples[i] = (int16_t)val;
}
}
// 发送对齐后的数据(丢弃奇数尾字节)
i2s_write(NS4168_I2S_PORT, buf, bytes_to_process, &written, portMAX_DELAY);
}
// 5. 关闭文件
md_deinit();
ESP_LOGI("WAV", "WAV播放结束");
}
void play_wav4(float volume)
{
uint8_t header[70];
size_t written = 0;
md_init("/extflash/ring2.wav");
md_read(header, sizeof(header));
ESP_LOG_BUFFER_HEX("WAV", header, sizeof(header));
ESP_LOGI("WAV", "WAV解析成功,开始播放");
uint8_t buf[1024];
int n;
while ((n = md_read(buf, sizeof(buf))) > 0) {
if (volume != 1.0f) {
int16_t *samples = (int16_t *)buf;
int num_samples = n / 2; // 16-bit PCM
for (int i = 0; i < num_samples; i++) {
// 浮点缩放
float scaled = (float)samples[i] * volume;
// 四舍五入(关键)
int32_t val = lrintf(scaled);
// 饱和限幅
if (val > 32767) val = 32767;
if (val < -32768) val = -32768;
samples[i] = (int16_t)val;
}
}
i2s_write(NS4168_I2S_PORT, buf, n, &written, portMAX_DELAY);
}
md_deinit();
ESP_LOGI("WAV", "WAV播放结束");
}
void play_wav5(float volume)
{
uint8_t header[70];
size_t written = 0;
md_init("/extflash/ring2.wav");
md_read(header, sizeof(header));
ESP_LOG_BUFFER_HEX("WAV", header, sizeof(header));
ESP_LOGI("WAV", "WAV解析成功,开始播放");
uint8_t buf[1024];
int n;
// 将浮点音量转换为 8-bit 定点(0~256)
int32_t vol_fixed = (int32_t)(volume * 256.0f);
while ((n = md_read(buf, sizeof(buf))) > 0) {
if (volume != 1.0f) {
int16_t *samples = (int16_t *)buf;
int num_samples = n / 2; // 16-bit PCM
for (int i = 0; i < num_samples; i++) {
// 原始 16-bit PCM
int32_t v = samples[i];
// 定点缩放:扩大到 24-bit 精度再缩回
v = (v * vol_fixed) >> 8;
// 饱和限幅
if (v > 32767) v = 32767;
if (v < -32768) v = -32768;
samples[i] = (int16_t)v;
}
}
i2s_write(NS4168_I2S_PORT, buf, n, &written, portMAX_DELAY);
}
md_deinit();
ESP_LOGI("WAV", "WAV播放结束");
}
void play_wav_p4(float volume)
{
uint8_t header[70];
size_t written = 0;
md_init("/extflash/ring2.wav");
md_read(header, sizeof(header));
ESP_LOG_BUFFER_HEX("WAV", header, sizeof(header));
ESP_LOGI("WAV", "WAV解析成功,开始播放");
uint8_t buf[1024];
int n;
// 如果音量是 1.0,直接原样播放,完全无损
bool bypass = (fabsf(volume - 1.0f) < 0.0001f);
// 简单 dither 强度(可以微调)
const float dither_amp = 1.0f / 512.0f; // 很小的抖动
while ((n = md_read(buf, sizeof(buf))) > 0) {
if (!bypass) {
int16_t *samples = (int16_t *)buf;
int num_samples = n / 2; // 16-bit PCM
for (int i = 0; i < num_samples; i++) {
// 提升到 float 32-bit 精度
float v = (float)samples[i];
// 音量缩放
v *= volume;
// 加一点点 dither,打散量化误差
// 简单线性同余随机数(不依赖 rand())
static uint32_t rnd = 0x12345678;
rnd = rnd * 1664525u + 1013904223u;
float noise = ((int32_t)(rnd >> 16) - 32768) * (dither_amp / 32768.0f);
v += noise;
// 高精度四舍五入
float v_rounded = floorf(v + 0.5f * (v >= 0 ? 1.0f : -1.0f));
// 饱和限幅
if (v_rounded > 32767.0f) v_rounded = 32767.0f;
if (v_rounded < -32768.0f) v_rounded = -32768.0f;
samples[i] = (int16_t)v_rounded;
}
}
i2s_write(NS4168_I2S_PORT, buf, n, &written, portMAX_DELAY);
}
md_deinit();
ESP_LOGI("WAV", "WAV播放结束");
}
#include <stdbool.h>
#include "driver/i2s.h"
// 简单的 32-bit 伪随机数,用于 dither
static inline uint32_t prng32(void)
{
static uint32_t s = 0x12345678u;
s = s * 1664525u + 1013904223u;
return s;
}
void play_wav_ring_with_volume(float volume)
{
const char *TAG = "WAV_RING";
uint8_t header[70];
size_t written = 0;
// 初始化并读取 WAV 头
md_init("/extflash/ring2.wav");
md_read(header, sizeof(header));
ESP_LOG_BUFFER_HEX(TAG, header, sizeof(header));
ESP_LOGI(TAG, "WAV解析成功,开始播放");
uint8_t buf[1024];
int n;
// 音量为 1.0 时完全绕过处理,保证绝对无损
bool bypass = (fabsf(volume - 1.0f) < 0.0001f);
// dither 强度(非常小,人耳几乎感知不到)
const float dither_amp = 1.0f / 4096.0f; // 可微调
while ((n = md_read(buf, sizeof(buf))) > 0) {
if (!bypass) {
int16_t *samples = (int16_t *)buf;
int num_samples = n / 2; // 16-bit PCM
for (int i = 0; i < num_samples; i++) {
// 提升到 float 32-bit 精度
float v = (float)samples[i];
// 音量缩放
v *= volume;
// 三角 dither(TDPF):(rand1 - rand2) * scale
uint32_t r1 = prng32();
uint32_t r2 = prng32();
float noise = ((int32_t)(r1 & 0xFFFF) - (int32_t)(r2 & 0xFFFF))
* (dither_amp / 65536.0f);
v += noise;
// 高精度四舍五入
float v_rounded = floorf(v + 0.5f * (v >= 0.0f ? 1.0f : -1.0f));
// 轻微 soft-clip,避免极端削波毛刺
if (v_rounded > 32767.0f) {
float over = v_rounded - 32767.0f;
v_rounded = 32767.0f + over * 0.01f; // 把硬削波变成软削波
} else if (v_rounded < -32768.0f) {
float under = v_rounded + 32768.0f;
v_rounded = -32768.0f + under * 0.01f;
}
// 最终饱和限幅
if (v_rounded > 32767.0f) v_rounded = 32767.0f;
if (v_rounded < -32768.0f) v_rounded = -32768.0f;
samples[i] = (int16_t)v_rounded;
}
}
// 写入 I2S
i2s_write(NS4168_I2S_PORT, buf, n, &written, portMAX_DELAY);
}
md_deinit();
ESP_LOGI(TAG, "WAV播放结束");
}