KDocs 研发资料库
  • 音频技术应用文档入口汇总
  • 杰理WINDOWS环境安装及其他事项
  • LINUX安装和编译
  • Linux环境编译SDK教程(ubuntu18.04)
  • 音频类补丁汇总表
  • 认证资料获取汇总(最新资料后台会更新)
  • 《Hi-Res申请指南v1.0》
  • FreeRTOS 开源协议版本
  • uCOS 开源协议版本信息
  • 各个模块关键资料汇总
  • 软件调试方法
  • Makefile编译
  • 提bug的规范
  • 常规设备使用指导
  • 对外特别发布开发注意点信息汇总
  • AD697X软件问题整理
  • AC700N软件问题整理
  • AC701X耳机软件问题整理
  • 951&961软件问题整理
  • AC104X软件问题整理
  • AC128N问题整理文档
  • 可视化工具调试问题整理
  • 耳机资料汇总
  • AC704N、 AC705N相关应用
  • AC695N&AC696N&AC608N音箱问题整理
  • JL701N音箱问题整理
  • AC692X软件问题整理
  • AC706N音箱问题整理
  • JL701N音箱问题整理(可视化工具SDK)
  • JL703N音箱问题整理
  • JL709N(JL689N)音箱问题整理
  • JL7094系列-入门到进阶开发指导
  • AC210N音箱问题整理
  • 无线麦系列版本支持指南
  • AW32N无线麦问题整理
  • lAD697X&JL701X 无线麦软件共性问题整理
  • AC706N无线麦问题整理
  • AW36N无线麦问题整理
  • AC710X无线麦问题整理
  • 唤醒源获取及唤醒IO设置
  • 电源
  • LVD
  • 长按复位配置方法
  • 低功耗应用
  • TIMER
  • UART
  • SPI
  • 内部FLASH空间添加用户自定义区域
  • IIC
  • GPIO
  • MCPWM
  • SD&U盘
  • 时钟&晶振
  • GPCNT
  • IO口crossbar映射功能
  • 文件系统使用总结
  • 复用应用
  • LINEIN_AUX相关应用
  • IIS音频接口应用
  • SPDIF模式相关(HDMI_ARC、光纤、同轴)
  • PDM LINK
  • 外设相关应用
  • 蓝牙相关
  • 杰理蓝牙距离调试文档
  • TWS耳机配对方式使用及生产把控指南
  • GFP谷歌快连及其认证流程指引
  • 蓝牙发射器(bt_emitter)相关
  • BLE应用
  • 关于提升BLE传输速率说明
  • BLE测试数据
  • MUSIC 模式相关应用
  • MIDI音乐实现
  • PC相关应用
  • FM相关应用
  • AC695N_AC696N FM天线ESD静电防护措施说明V2.0_20250107
  • fm应用详细设计说明书
  • 杰理FM硬件指南-20230315
  • ADC模块
  • AUDIO相关应用
  • 新Tools的音效调试使用
  • 混响应用使用说明
  • 音效调式
  • 音箱耳机低音小音量增强实现方法
  • DAC输出功率调试
  • DAC性能测试指南
  • 针对各类底噪、电流声分析流程
  • 录音相关应用
  • OPUS编解码相关
  • RTC相关应用
  • 升级相关
  • UI相关应用
  • 通话调试流程指南
  • 通话相关应用
  • 省电容mic使用手册
  • 如何测试验证手表通话系统的语音失真 V1.0
  • AC695X_AC696X_AD697X 常见通话问题处理参考资料
  • 通话调试问题确定步骤
  • 内置触摸调试指南
  • 低功耗内置触摸介绍V2.2
  • 触摸相关文档
  • AC697X、AC698X内置触摸调试及整改方法V1.0
  • 产测相关应用
  • AC63X BLE DUT测试介绍
  • 兆华序列使用简易说明
  • JL_1T2蓝牙测试盒音频文件更换说明
  • ENC相关应用入口汇总
  • ANC相关应用入口汇总
  • JL DMS通话调试手册-V3.9.4
  • ANC-ENC结构设计要点-20240407
  • 半入耳式ANC耳机器件选型与结构设计指南V1.0
  • JL 耳道自适应调试指南
  • ANC相关问题处理
  • ANC调试文档
  • 评估蓝牙耳机通话上行降噪性能的方法 V1.0 (2)
  • FCC 或者 BQB 认证相关应用
  • BQB profile 测试中常遇到的问题点汇总
  • 蓝牙参数整理20210811
  • 杰理科技认证问题参考
  • 无线音频产品开发指南
  • 无线麦系列从入门到进阶开发指导
  • 量产烧录相关说明
  • 一拖八烧录器远程过程调用说明
  • 量产相关建议
  • EffectTool(eq)调试工具说明文档
  • AC897AD697系列的EQ调试方法-V3
  • 杰理调音指南-20220622
  • 音效模块总结
  • 简要调音步骤指引(可视化)
  • 耳机充电与通信调试指南
  • 充电仓硬件调试指南
  • 充电仓软件问题整理
  • AC951常用说明
  • AC951_AC961_开发板使用说明V1.0
  • 彩屏仓AC982 入门指导及FAQ
  • 蓝牙音频APP相关应用
  • 手机APP的简介和开发流程
  • 蓝牙音频系列-硬件设计指南
  • 静电测试及整改方法-20220706
  • TWS耳机底噪整改指南V1.2
  • 杰理蓝牙距离调试文档
  • 蓝牙RF硬件设计与调试V1.1-20250219
  • AC695N_AC696N FM天线ESD静电防护措施说明V2.0_20241228
  • 音效调试指南
  • 无线话筒K歌音箱标准开发流程
  • 杰理科技 700N TWS耳机产品标准-202303
  • 杰理科技 701N 广播产品标准-202303
  • JL-HAT ANC高效产测自适应
  • 无线话筒测试板使用介绍
  • 一拖八频偏校准使用说明
  • 领夹麦产测工具使用介绍
  • Dongle量产测试说明
  • Dongle+Tws或头戴式 SDK通过工具绑定配对功能说明文档
  • 双麦ENC产测宏配置验证方法 V1.0
  • JL耳机方案麦克风声学测试指令_V1.0
  • JL耳机方案麦克风声学测试指令_V2.0
  • JL_ENC自动测试命令_V0.0.1
  • JL蓝牙测试盒用例说明
  • LDAC自测环境搭建_ubuntu
  • 延迟小板使用手册
  • 杰理库文件的制作及添加实现过程
  • 关于 AC635N&AC636N系列免晶振兼容性补丁 说明
  • 关于 AC63系列VM兼容性修复补丁_20250224 说明
  • 关于 AC632N &AE632N_ 蓝牙& 低功耗兼容性修复补丁 说明
  • AD698N和AC638N电压优化补丁V3说明
  • 关于 AC635N&AC636N漏电和E&G版兼容联合补丁 说明
  • AD698N和AC638N电压优化补丁V2说明
  • 702相关应用
  • 2024-4-30 发布歌词显示SDK开发注意事项
  • BQB RF & FCC 认证测试固件问题汇总及源码
  • BQB profile 测试中常遇到的问题点汇总
  • 进dut测试模式方法及固件
  • 704-频偏修改bin文件合集
  • 701手表BQB PROFILE认证注意事项
  • 认证的干扰规避整改
  • 海外CRA认证报告生成说明
  • JL700N可视化工具问题处理文档
  • JL701X可视化工具问题处理文档
  • JL701N_earphone_release_v1.6.0_ANC 低功耗问题补丁
  • JL708X可视化工具问题处理文档
  • JL709N可视化工具问题处理文档
  • JLstudio可视化工具使用说明
  • 一拖二-串口_蓝牙_app在线EQ、通话参数及调试方式和内置触摸调试
  • 可视化JL701M_DONGLE_SDK添加MIC为发射源
  • 可视化JL701M_DONGLE_SDK添加按键切换usb audio与MIC发射源
  • JL701M_DONGLE_SDK_1.0.0_dongle输入linein输出dac
  • 701可视化_a2dp_to_le_demo说明
  • 耳机系列demo汇总
  • JL7018F&JL7006F_7003F AUX IIS
  • 一键连接应用
  • 普通SDK动态开关一拖二功能
  • 蓝牙分析仪(EllisysBluetoothAnalyzer)抓包例程
  • TWS配对常见应用
  • APP自定义按键的demo
  • AC700N 播歌双EQ DEMO示例及说明
  • AC698V164 MIC与蓝牙音乐共同输出DAC demo
  • 添加第三方算法以及多算法授权流程
  • 空间音频配置方法
  • 普通高低电平TWS耳机控制通讯
  • 入耳检测状态更新demo解析
  • 资源优化(非可视化的SDK)
  • 资源优化(可视化SDK)
  • 翻译耳机、语音识别应用demo
  • 耳机抗干扰问题排查和优化
  • AC698D2 抗干扰优化
  • AC696X软件问题整理
  • AC695X软件问题整理
  • AC695X软件问题整理_1722249702
  • AC608X软件问题整理
  • JL701N_2T1_DUPLEX 应用软件问题汇总
  • le_audio_1.0.0_patch05 以及之前的版本IRkey 功能异常修改
  • AC706N-le_audio-200SDK有线_无线麦录音demo
  • jl701n_le_audio_v200patch02 IO串口升级修改串口单次发送字节长度的修改办法
  • 703创建IIS 输入dac输出音频流demo
  • 703将媒体流音频同时上传PC_mic录音demo实现
  • 703四路IIS输入输出demo
  • 底噪调试
  • AW328A烧录说明
  • 文档合集
  • AC706N_wireless_mic_release_PATCH_20240712 补丁 说明
  • AC706N_wireless_mic_release_PATCH_20240726 补丁 说明
  • AC706N_wireless_mic_release_V1.0.0、1.0.1、1.0.2 补丁 说明
  • AC706N_wireless_mic_release_V1.1.2 中的2T1应用 RX端不绑定配对,TX端实现绑定配对的修改方法
  • 各系列省晶振实现方法
  • iis tx rx说明文档
  • spdif信号
  • 蓝牙电话本
  • 安卓手机同步时间补丁
  • CDC配置
  • PC模式下增加Teams唤起功能
  • 杰理HDMIARC开发实现过程
  • AC701N patch06之前的版本用第三方算法节点实现新版本的pcm delay节点功能
  • (K歌音箱)各SDK音效配置相关说明
  • 长文件录音
  • 点阵屏歌词字库相关(蓝牙歌词,普通歌词)
  • 字模提取工具说明
  • 音箱可视化彩屏-入门到进阶开发指导
  • 彩屏LVGL UI设计及工具使用
  • 使用内部UI框架进行UI调试的系列步骤
  • AD101C使用强制升级工具spi 烧录不了问题处理(测试版本)
  • AD161扩展灯板说明文档
  • 歌词显示器开发注意项
  • 使用硬件数字增益控制通话下行音量(可视化)
  • 杰理小牛产测资料
  • ENC配置说明
  • 通话调试流程指引
  • ANC相关功能demo
  • 无线麦硬件资料汇总
  • 无线麦相关应用
  • 无线麦复杂功能补丁包汇总
  • 无线话筒形态SDK程序&开发经验&问题点分享
  • 双模式结合应用问题汇总
  • 无线麦距离优化案例
  • 无线麦系列中的双工形态(duplex)演变开发对讲机方案开发资料
  • 音箱入门到进阶指导文档(编写中)
  • 蓝牙耳机品控和生产技巧
  • 982过_欠压标志获取异常
  • 如何获取最新涂鸦SDK及库文件
  • 无线话筒K歌音响-调音标准流程
  • 无线话筒产测流程

MUSIC 模式相关应用

📎 原始文档:https://www.kdocs.cn/l/ckoROeH2VL48 MUSIC 模式相关应用

一. 设备类

  1. 设备剩余空间获取(LZK)
int fet_free_space(const char *path, u32 *space);   函数返回值单位为KB

例子:

//sd0
u32 free_space = 0;
fget_free_space("storage/sd0/C/",&free_space);
printf("free_space %d\n",free_space);

//sd1
u32 free_space = 0;
fget_free_space("storage/sd1/C/",&free_space);
printf("free_space %d\n",free_space);

//USB
u32 free_space = 0;
fget_free_space("storage/udisk0/C/",&free_space);
printf("free_space %d\n",free_space);

打印信息如下

printf("free_space %d KB - %d MB\n",free_space,free_space/1024);

image PS:1、AC692系列 —— 修改看:AC692X软件问题整理 122点
2、AC696 补丁如下:
image

2. 获取设备总容量(LZK)

struct vfs_partition *fget_partition(const char *path);//获得分区part
信息在以下返回结构体中:
image 例子

//sd0
struct vfs_partition *part;
part = fget_partition("storage/sd0/C/");
printf("total_size %dKB - %d MB,clust size %d\n",part->total_size,part->total_size/1024,part->clust_size);

//sd1
struct vfs_partition *part;
part = fget_partition("storage/sd1/C/");
printf("total_size %dKB - %d MB,clust size %d\n",part->total_size,part->total_size/1024,part->clust_size);

//USB
struct vfs_partition *part;
part = fget_partition("storage/udisk0/C/");
printf("total_size %dKB - %d MB,clust size %d\n",part->total_size,part->total_size/1024,part->clust_size);

打印信息如下

printf("free_space %d KB - %d MB\n",free_space,free_space/1024);

imageimage

相关补丁

  • AC695N_soundbox_sdk_release_3.1.0 【金山文档】 获取设备总容量_AC695N_soundbox_sdk_release_3.1.0

3.设备支持

支持文件夹个数:2^32个  
支持文件个数:2^32 个  
支持文件深度:9层   
支持最大容量:128PB
支持系统:支持fat12\16\32\exfat

二. 文件系统基础接口

  1. 文件操作接口 SDK自带的文档:《文件系统接口设计说明文档》有说明

fopen 创建或打开文件

可设置不同读写属性。

FILE * fopen(const char * path,const char * mode);
    -- path: 文件路径,如:"storage/sd0/C/test.txt"
    -- mode: 文件打开方式,例如:
             "r" 以只读方式打开文件,该文件必须存在。
             "r+" 以可读写方式打开文件,该文件必须存在。。
             "w" 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
             "w+" 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
             "a" 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
             "a+" 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。(原来的EOF符不保留)
             "wb" 只写打开或新建一个二进制文件,只允许写数据。
             "wb+" 读写打开或建立一个二进制文件,允许读和写。
             "ab" 追加打开一个二进制文件,并在文件末尾写数据。
             "ab+"读写打开一个二进制文件,允许读,或在文件末追加数据。   
    --返回值: 如果文件打开失败则返回 NULL

fopen播放文件参考

static FILE *open_file = NULL;//加到函数前,如下:
static void open_file_play_evt_handler(void *priv, int argc, int *argv)
{
    u8 event = (u8)argv[0];
    if (event == 
_DEC_EVENT_END) {
        open_file_close();
    }
}

    int ret = 0;
    char path[64] = {0};
    sprintf(path,"%s","storage/fat_nor/C/JL_REC/AC69****.***");//*:通配符
    file_dec_close();
    open_file = NULL;
    open_file = fopen(path, "r");
    if(open_file){
        ret = file_dec_create(NULL, open_file_play_evt_handler);
        if(!ret){
            file_dec_open(open_file, NULL);
        }
    }

f_close 关闭打开的文件

int (fclose)(FILE *);

f_read 读取文件

    case KEY_FREAD_FILE:
        printf("KEY_FREAD_FILE\n");
        char user_file_path[48] = "storage/udisk0/C/1.mp3";         //路径
        FILE *fd = fopen(user_file_path, "r");                      //检查文件是否存在
        if (!fd) {
            r_printf("open %s file err!!!\n",user_file_path);
            break;
        }
        u8 buf[512]={0};
        int len;
        fseek(fd,128,SEEK_CUR);                                     //偏移128个字节
        len = fread(fd, buf, sizeof(buf));                          //读取内容
        r_printf("read file len %d",len);
        put_buf(buf,sizeof(buf));                                   //打印每一个字节
        //r_printf("read file data %s",buf);                        //不建议用这个方式去打印buf
        fclose(fd);                                                 //关闭
        break; 

fdelete删除文件

        char user_file_path[48] = "storage/udisk0/C/1.mp3";         //路径
        FILE *fd = fopen(user_file_path, "r");                      //检查文件是否存在
        if (!fd) {
            r_printf("open %s file err!!!\n",user_file_path);
            break;
        }
        fdelete(fd);//删除文件

file_manager_delete_dir删除文件夹

/* --------------------------------------------------------------------------*/
/**
 * @brief    文件删除统一处理
 *
 * @param path 扫描路径名
 * @param param 配置参数
 * @param dir_flag 是否删除文件夹标志
 * @note 加速处理:(删除文件的时候使用)从前往后依次删除. 文件夹必须从后往前删。
 *
 * @return  0成功, 其他失败
 */
/* ----------------------------------------------------------------------------*/
int file_manager_delete_deal(char *path, char *param, u8 dir_flag)
{
    u16 folder_total_file = 0;
    struct vfscan *fsn = NULL;
    FILE *d_f = NULL;

    fsn = fscan(path, param, 9);
    if (fsn == NULL) {
        r_printf(">>>[test]:err!!!!!! fsacn fsn fail\n");
        return 1;
    }
    folder_total_file = fsn->file_number;
    y_printf(">>>[test]:total = %d\n", folder_total_file);
    for (int i = folder_total_file; i >= 1; i--) {
        if (!dir_flag) {
            d_f = fselect(fsn, FSEL_BY_NUMBER, folder_total_file - i + 1); //加速处理,不用找到最后一个文件。
        } else {
            d_f = fselect(fsn, FSEL_BY_NUMBER, i);
        }
        if (d_f == NULL) {
            r_printf(">>>[test]:err!! select file err\n");
            return 1;
        }
        putchar('D');
        int d_err = fdelete(d_f);
        if (d_err) {
            r_printf(">>>[test]:err!! delete file err\n");
            return 1;
        }
        d_f = NULL;
    }
    return 0;
}


/*----------------------------------------------------------------------------*/
/** @brief:文件夹删除处理
    @param:dev_logo :设备logo  folder:文件夹路径(短名) folder_len:文件夹路径长度
    @return:
    @author:phewlee
    @note:
    @date: 2021-05-21,10:16
*/
/*----------------------------------------------------------------------------*/
int file_manager_delete_dir(char *dev_logo, char *folder, u16 folder_len)
{
    int err = 0;
    struct __dev *dev;
    char path[128] = {0};

    static const u8 delete_file_param[] = "-t"
                                          "ALL"
                                          " -sn -r";

    static const u8 delete_folder_param[] = "-t"
                                            "ALL"
                                            " -sn -d -r";

    dev = dev_manager_find_spec(dev_logo, 0);
    if (dev == NULL) {
        r_printf(">>>[test]:errr!!!!!!!!! not find dev\n");
        return 1;
    }
    char *root_path = dev_manager_get_root_path(dev);
    memcpy(path, root_path, strlen(root_path));
    memcpy(path + strlen(root_path), folder, folder_len);
    r_printf(">>>[test]:path = %s\n", path);
    err = file_manager_delete_deal(path, (char *)delete_file_param, 0);
    if (err) {
        r_printf(">>>[test]:errr!!!!!!!!! delete file deal fail\n");
        return 1;
    }
    err = file_manager_delete_deal(path, (char *)delete_folder_param, 1);
    if (err) {
        r_printf(">>>[test]:errr!!!!!!!!! delete folder  deal fail\n");
        return 1;
    }
    FILE *folder_f = fopen(path, "r");
    if (folder_f == NULL) {
        r_printf(">>>[test]:err open folder\n");
        return 1;
    }
    err = fdelete(folder_f);
    return err;
}

f_write 写文件

f_seek 移动当前文件操作指针到指定位置

f_seek加速:改善f_seek函数在偏移量大的情况下,执行时间偏长问题
换库:
【金山文档】 system_AC695N_soundbox_310_2022.8.25_添加seek和上下曲加速
https://kdocs.cn/l/cr5vNaSpMSvd
修改:
const int FILT_0SIZE_ENABLE = 1;
imageimageimage

#define FILE_DEC_SAVE_FAT_TABLE_SIZE  1024       
//......
    if(dec->fat_table == NULL){
        dec->fat_table = zalloc(FILE_DEC_SAVE_FAT_TABLE_SIZE);
    }
    else{
        printf("warn\n");
    }
    fsave_fat_table(dec->file,FILE_DEC_SAVE_FAT_TABLE_SIZE,dec->fat_table);

fpos - 获取当前文件指针。

flen - 获取文件大小。

 int fsize = flen(d_f);

frename - 重命名文件

int frename(FILE *file, const char *path);

//demo
char open_path[32] = "storage/sd0/C/3.txt";
char rename_path[16] = "4.txt";
FILE *file;
file = fopen(open_path, "r"); //只能用 r 打开
//重命名
if(file){
    printf("open file ok\n");
    extern int frename(FILE *file, const char *path);
    frename(file,rename_path);
}
fclose(file);

fscan 扫描文件夹

void *dev = dev_manager_find_active(1);
if (!dev) {
    //printf("No active device found");
    return;
}
 
struct vfscan *root_fsn = fscan(dev_manager_get_root_path(dev), MUSIC_SCAN_PARAM, 1);

fscan的长文件夹名的路径要求为 (USB路径/ “\U”002Language/“\U”006Section/)
image 对应十六进制字符串为:
73 74 6F 72 61 67 65 2F 75 64 69 73 6B 30 2F 43
5C 55 30 00 30 00 32 00 4C 00 61 00 6E 00 67 00
75 00 61 00 67 00 65 00 2F 00
5C 55 30 00 30 00 36 00 53 00 65 00 63 00 74 00
69 00 6F 00 6E 00 2F 00

1. 完整解码结果
第一部分:ASCII/UTF-8 路径
原始数据:
73 74 6F 72 61 67 65 2F 75 64 69 73 6B 30 2F 43
转换结果:
storage/udisk0/C
分析:

标准Unix风格路径,指向存储设备的C分区或目录。

第二部分:UTF-16LE 字符串
原始数据:
5C 55 30 00 30 00 32 00 4C 00 61 00 6E 00 67 00 75 00 61 00 67 00 65 00 2F 00
转换结果:
\U002Language/
关键点:

\U002:可能是资源标识符(类似Windows资源格式)。

Language:表示语言分类目录。

末尾/:路径分隔符。

第三部分:UTF-16LE 字符串
原始数据:
5C 55 30 00 30 00 36 00 53 00 65 00 63 00 74 00 69 00 6F 00 6E 00 2F 00
转换结果:
\U006Section/
关键点:

\U006:资源编号(第6条)。

Section:表示章节或区块分类。

设置文件创建时间/修改录音创建的文件的时间

//! 创建文件前先传好
void fat_set_datetime_info(u16 year,u8 month,u8 day,u8 hour,u8 minute,u8 second);

补丁:
AC695N&AC696N&AC608N音箱问题整理.otl
AC695N_音箱3.0.4SDK_EXFAT文件系统使用修复:【金山文档】 system_AC695N_304_2023_2_25_支持exfat创建时间 https://kdocs.cn/l/cicOGoy1yhzy
【金山文档】 system_AC695N_310_2023_4_13_支持exfat创建时间 https://kdocs.cn/l/cgX7ThkllKJS

  • 设置卷标 不能超过11字节(标准文件系统固定最长是11字节)。
int fset_vol(const char *path, const char *name);//设置卷标

部分SDK还需要打开下面这个写fats的开关
imageimage


        #define FLAG_NAME "JL123456789"//长度不能超过11个
        #define STRL_EN  "\0"
        #define FLAG_NAME_LEN 11
        const char name[] =FLAG_NAME STRL_EN;//不加结束符 编译器有概率不补/0
        // const char *name = FLAG_NAME STRL_EN;//不加结束符 编译器有概率不补/0
        // const char name[FLAG_NAME_LEN+1] = FLAG_NAME;
        r_printf("dev_manager_add name len %s %d\n",name, strlen(name));
        ASSERT(strlen(name) <= FLAG_NAME_LEN){
            r_printf("name too long\n");
        }
        r_printf("dev_manager_add %s %s %s %s\n", p->logo, p->storage_path, p->fs_type,name);
        int ret = fset_vol(p->root_path, name);
        r_printf("fset_vol %s RET %d\n", p->root_path, ret);
        if(ret){
            r_printf("fset_vol err\n");
        }

image 701 142 需要换库不然exfat文件系统更改不了卷标名资料传送门

文件/文件夹名

//AC695 长文件名 demo
    u8 path_buf[256] = {0};
    int len = fget_path(music_player_get_file_hdl(),path_buf,sizeof(path_buf),1);
    
    if ((path_buf[0] == '\\') && (path_buf[1] == 'U')) {
        unicode = 1;
        len -= 2;
        printf("cur path  = %s, len = %d, unicode = %d\n", path_buf + 2, music_file_name_len, unicode);
    } else {
        printf("cur path  = %s, len = %d, unicode = %d\n", path_buf, music_file_name_len, unicode);
    }

//文件夹名 demo
    char path_buf[512] = {0};
    int len_1 = fget_path(music_player_get_file_hdl(),path_buf,sizeof(path_buf),1);
    put_buf(path_buf,sizeof(path_buf));

//是否过滤隐藏 和 .开头名名的字文件 1:过滤 0不过滤
    hidden_file(0);

长文件名测试程序

///////////////////长文件名创建和重命名接口测试/////////////////////////////////////////////
/*----------------------------------------------------------------------------*/
/** @brief:文件创建及重命名接口测试
  @param: 测试fopen, frename.
  @return:
  @author: phew lee .fy
  @note:
  @date: 2021-1-26,14:54
 */
/*----------------------------------------------------------------------------*/

#ifdef LONG_FOPEN_RENAME_TEST
void long_fopen_rename_test()
{
#if 0
    static void *file;
    u8 buf[256] = {0};
    for (int i = 0; i < 256; i++) {
        buf[i] = (i & 0xff);
    }
    int len = 0;
    static void *file_d;
    char path_d[64] = "storage/virfat_flash/C/delete.txt";
    y_printf(">>>[test]:open create 1111\n");
    file_d = fopen(path_d, "w+");
    y_printf(">>>[test]:dwrite\n");
    len = fwrite(file_d, buf, 256);
    fclose(file_d);
    file_d = fopen(path_d, "r");
    memset(buf, 0, 256);
    r_printf(">>>[test]:read......\n");
    for (int i = 0; i < 2; i++) {
        len = fread_fast(file_d, buf, 256);
        if (len == 256) {
            put_buf(buf, 256);
        } else {
            r_printf(">>>[test]:len = %d\n", len);
        }
    }
    fclose(file_d);
#endif

#if 1
#define N 64
    char path[256] = "storage/sd0/C";
    /* char file_path[128] = {'/','M', 'U', 'S', 'I', 'C', 'h', '/', '\\', 'U', 0x61, 0x0, 0x62, 0x0, 0x63, 0x0, 0x64, 0x0, 0x65, 0x0, 0x66, 0x0, 0X67, 0x0, 0x68, 0x0, 0x69, 0, '/', 0, 't', 'e', 's', 't', '.', 'm', 'p' , '3', 0}; */
    /* char file_path[128] = {'/','M', 'U', 'S', 'I', 'C', '/', '\\', 'U', 0x31, 0x0, 0x32, 0x0, 0x33, 0x0, 0x34, 0x0, 0x35, 0x0, 0x36, 0x0, 0X37, 0x0, 0x38, 0x0, 0x39, 0, '/', 0, '\\', 'U', 0x31, 0x0, 0x32, 0x0, 0x33, 0x0, 0x34, 0x0, 0x35, 0x0, 0x36, 0x0, 0X37, 0x0, 0x38, 0x0, 0x39, 0x0, 0x31, 0, 0x32, 0, 0x33, 0, 0x2E, 0x0, 0x6D, 0x0, 0x70, 0x0, 0x33, 0}; */
    /* char file_path[56] = { '\\', 'U', 0x4B, 0x6D, 0xD5, 0x8B, 0x1B, 0x52, 0xFA, 0x5E, 0x7F, 0x95, 0x87, 0x65, 0XF6, 0x4E, 0x0D, 0x54, 0x2E, 0, 0x6D, 0, 0x70, 0, 0x33, 0, '&', '&', 0xB2, 0xE2, 0xCA, 0xD4, 0xB4, 0xB4, 0xBD, 0xA8, 0xB3, 0xA4, 0xCE, 0xC4, 0xBC, 0xFE, 0xC3, 0xFB, 0, 0}; // 测试创建长文件名.mp3 + '&'+ '&' + GBK */
    /* char file_path[56] = { '\\', 'U', 0x4B, 0x6D, 0xD5, 0x8B, 0x1B, 0x52, 0xFA, 0x5E, 0x7F, 0x95, 0x87, 0x65, 0XF6, 0x4E, 0x0D, 0x54, 0x2E, 0, 0x74, 0, 0x78, 0, 0x74, 0, '&', '&', 0xB2, 0xE2, 0xCA, 0xD4, 0xB4, 0xB4, 0xBD, 0xA8, 0xB3, 0xA4, 0xCE, 0xC4, 0xBC, 0xFE, 0xC3, 0xFB, 0, 0}; // 测试创建长文件名.txt + '&'+ '&' + GBK */
    /* char file_path[56] = { '\\', 'U', 0x4B, 0x6D, 0xD5, 0x8B, 0x1B, 0x52, 0xFA, 0x5E, 0x7F, 0x95, 0x87, 0x65, 0XF6, 0x4E, 0x0D, 0x54, 0x2E, 0, 0x74, 0, 0x78, 0, 0x74, 0}; // 测试创建长文件名.txt */
    /* char file_path[56] = { '\\', 'U', 0x60, 0x4F, 0x7D, 0x59, 0x40, 0x54, 0x2E, 0, 0x6D, 0, 0x70, 0, 0x33, 0}; //你好呀.mp3 */
    /* char file_path[56] = {0xB1, 0xA6, 0xB1, 0xA6, 0xBF, 0xDE, 0xC1, 0xCB, 0x2E, 0x74, 0x78, 0x74, 0}; // 测试创建长文件名.txt */
    /* char file_path[] = {'/','\\', 'U', 0xD9, 0x8F, 0x2A, 0x4E, 0x8e, 0x7f, 0x7d,0x59,0x84,0x76,0x1f,0x75,0x3b,0x6d,0x2D,0x00,0x81,0x68,0xE5,0x5D,0x2E, 0x0, 0x6D, 0x0, 0x70, 0x0, 0x33, 0}; // 这个美好的生活-梁工.mp3   */
    char file_path[128] = {'/', '\\', 'U', 0xD9, 0x8F, 0x16, 0x4E, 0x4C, 0x75, 0x1F, 0x77, 0x27, 0x59, 0x2D, 0x00, 0x81, 0x68, 0xE5, 0x5D, 0x2E, 0x0, 0x6D, 0x0, 0x70, 0x0, 0x33, 0}; // 123.mp3
    /* char file_path[128] = {'\\', 'U', 0x31, 0x0, 0x32, 0x0, 0x33, 0x0, 0x34, 0x0, 0x35, 0x0, 0x36, 0x0, 0X37, 0x0, 0x38, 0x0, 0x39, 0x0, 0x31, 0, 0x32, 0, 0x33, 0, 0x2E, 0x0, 0x6D, 0x0, 0x70, 0x0, 0x33, 0}; */
    memcpy(path + strlen(path), file_path, sizeof(file_path));
#if 0
    FILE *file = fopen("storage/sd0/C/music2.txt", "w+");
    char *buf = malloc(N);
    memset(buf, 0, N);
    for (int i = 0; i < N; i++) {
        buf[i] = (i & 0xff);
    }
    int wlen = fwrite(file, buf, N);
    r_printf(">>>[test]:wlen = %d\n", wlen);
    if (wlen != N) {
        r_printf(">>>[test]:err write\n");
    }
    memset(buf, 0, N);
    fseek(file, 0, SEEK_SET);
    int rlen = fread(file, buf, N);
    y_printf(">>>[test]:rlen = %d\n", rlen);
    if (rlen != N) {
        r_printf(">>>[test]:err read\n");
    }
    for (int i = 0; i < N; i++) {
        if (buf[i] != (i & 0xff)) {
            r_printf(">>>[test]:errr buf[%d] = %d\n", i, buf[i]);
            /* return; */
        }
    }
    free(buf);
    buf = NULL;
    fclose(file);
    file = NULL;
    return;
#endif

#if 0
    /////////////////////////////////////////////
    FILE *file = fopen("storage/sd0/C/music123.txt", "w+");
    /* FILE *file = fopen(path, "r"); */
    if (file == NULL) {
        r_printf(">>>[test]:open errrr!!!!!\n");
    }
    fclose(file);
    file = NULL;
    file = fopen("storage/sd0/C/music123.txt", "r");
    /* int err = frename(file, file_path); */
    int err = frename(file, "3444.txt");
    if (err == 0) {
        y_printf(">>>[test]:okk!!!!!!!!!!!\n");
    } else {
        y_printf(">>>[test]:error!!!!!!!!!!!\n");
    }
    fclose(file);
    file = NULL;
#else
    FILE *file = fopen(path, "w+");
    /* FILE *file = fopen("storage/udisk0/C/test.txt", "w+"); */
    if (file) {
        fclose(file);
        file = NULL;
    } else {
        y_printf(">>>[test]:errror open long name\n");
    }
#endif
#endif
}
#endif


void fatfs_test_open(void) 
{

#if 1
    char path[256] = {"storage/udisk0/C/999999/"};
    int path_len = strlen(path);

    char dir[64] = {"升级plus再次"};
    u8 utf8_flag = utf8_check(dir, sizeof(dir));
    if (!utf8_flag) {
        y_printf("\n >>>[test]:func = %s,line= %d\n",__FUNCTION__, __LINE__);
        return;
    }
    u16 dir_unicode[64] = {0}; 
    int len = UTF82Unicode(dir, dir_unicode + 1, sizeof(dir));
    y_printf(">>>[test]:len = %d\n", len);
    dir_unicode[0] = 'U\\';
    dir_unicode[len + 1] = '/';
    put_buf(dir_unicode, (len + 2) * 2);
    memcpy(path + path_len, dir_unicode, (len + 2) * 2);

    memcpy(path + path_len + (len + 2) * 2, "ABC.mp3", strlen("ABC.mp3"));
    FILE *file = fopen(path, "w+");
    if (file) {
        r_printf(">>>[test]:open OK\n");
    } else {
        r_printf(">>>[test]:open err\n");
    }
    return;

#else
    /* for (int i = 0; i < 20; i++) */
    {
        FILE *file = fopen("storage/udisk0/C/JL_REC/AC69****.mp3", "w+");
        /* FILE *file = fopen("storage/udisk0/C/AAAA/BBBB/CCCC/AS11.txt", "r"); */
        if (!file) {
            y_printf(">>>[test]:errr open !!!!!!!!!!\n");
            while(1);
        }
        fclose(file);
    }
#endif
}

695创建打开长文件名

【金山文档 | WPS云文档】 system_695v310_创建长文件夹名称乱码修复_20241225
https://kdocs.cn/l/coL5vP8v3GFY

696创建打开长文件名

换库文件
【金山文档 | WPS云文档】 system_696v170_创建长文件夹名称乱码修复_20241225
https://kdocs.cn/l/cgjRcKP0MrLc
【金山文档】 system_AC696N_170_2023_7_14_添加支持多级长文件名创建
https://kdocs.cn/l/cr5sfKIUVlBv
编译不过在lib_system_config.c添加以下定义


const int FATFS_WRITE = 0; // 控制fatfs写功能开关。
const int FILT_0SIZE_ENABLE = 1; //是否过滤0大小文件
const int FATFS_LONG_NAME_ENABLE = 1;
const int FATFS_RENAME_ENABLE = 0;    // 是否使能文件重命名功能
const int FATFS_FGET_PATH_ENABLE = 0;
const int FATFS_SAVE_FAT_TABLE_ENABLE = 1;
//demo程序
    char path[384] = "storage/udisk0/C";
    char file_name[128] = {'/','\\','U',0x4B,0x6D,0xD5,0x8B,0x7F,0x95,0x87,0x65,0xF6,0x4E,0x39,0x59,0x0D,0x54,0x4B,0x6D,0xD5,0x8B,'/',0x00,'\\','U',0x4B,0x6D,0xD5,0x8B,0x1B,0x52,0xFA,0x5E,0x7F,0x95,0x87,0x65,0xF6,0x4E,0x0D,0x54,0x4B,0x6D,0xD5,0x8B,0x2E,0x00,0x74,0x00,0x78,0x00,0x74,0x00};
    memcpy(path + strlen(path), file_name, sizeof(file_name));
    printf("name == %s\n",path);
    FILE *file = fopen(path, "w");//r
    printf("name == %s\n",path); 
    if (file) {
        printf("open_file_ok\n");
        fclose(file);
        file = NULL;
    } else {
        printf("open_file_not_ok\n");
        y_printf(">>>[test]:errror open long name\n");
    }
//注意长文件名是Unicode码

image 网址:http://zhanzhang.cnzzla.com/tool/unicode.php

通过获取文件夹名称组成路径,对该文件夹进行单独扫盘

参考以下demo,该方法即通过fget_name获取文件夹名,组成扫盘路径,单独对该文件夹扫盘,获取该文件夹扫盘句柄
AC695N
该demo程序中替换了以下库文件:
【金山文档】 system_AC695N_310_2023_8_15_修复兼容fat32_exfat长短名文件夹和带点短名文件夹扫盘https://kdocs.cn/l/caucpvgqGavA
【金山文档】 AC695N_soundbox_sdk_release_3.1.0-支持对fat32和exfat系统下长短名文件夹扫盘处理【该demo程序中已替换上面的system.a文件】
AC695N_soundbox_sdk_release_3.1.0-支持对fat32和exfat系统下长短名文件夹扫盘处理.zip

AC696N
【金山文档】 ac696n_soundbox_sdk_v1.7.0-支持对长短名文件夹扫盘
ac696n_soundbox_sdk_v1.7.0-支持对长短名文件夹扫盘.zip

  1. 文件解码接口 SDK\apps\common\music\music_player.c
//music_player获取当前播放歌曲总时间
int music_player_get_dec_total_time(void)
//music_player获取当前播放循环模式
music_player_get_repeat_mode(void)
//music_player获取当前播放对应的music设备
char *music_player_get_cur_music_dev(void)
//music_player获取当前播放状态
int music_player_get_play_status(void)

音频文件获取信息

//*----------------------------------------------------------------------------*/
/**@brief    music 解码成功回调
   @param    priv:私有参数, parm:暂时未用
   @return
   @note     此处可以做一些用户操作, 如断点保存, 显示, 获取播放信息等
*/
/*----------------------------------------------------------------------------*/
static void music_player_play_success(void *priv, int parm)
{
    char *logo = music_player_get_dev_cur();
    log_i("\n\n----------------music_player_play_success----------------------\n");
    log_i("cur dev = %s\n", logo);
    log_i("total dev = %d\n", dev_manager_get_total(1));
    log_i("cur filenum = %d\n", music_player_get_file_cur());
    log_i("totol filenum = %d\n", music_player_get_file_total());
    log_i("totol time = %d\n", music_player_get_dec_total_time());
    log_i("sclust = %d\n", music_player_get_file_sclust());
    log_i("fsize = %d\n", music_player_get_file_fsize());
    log_i("dir_cur = %d\n", music_player_get_dir_cur());
    log_i("dir_total = %d\n", music_player_get_dir_total());
    log_i("file indir = %d\n", music_player_get_fileindir_number());
    music_file_name_len =  fget_name(music_player_get_file_hdl(), music_file_name, sizeof(music_file_name));
    int unicode = fget_name_type(music_file_name, music_file_name_len);
    log_i("cur file  = %s\n", music_file_name);
    log_i("\n");

    ///save breakpoint, 只保存文件信息
    if (music_player_get_playing_breakpoint(breakpoint, 0) == true) {
        breakpoint_vm_write(breakpoint, logo);
    }

    int analaz =  music_player_lrc_analy_start();
    ///show ui
    UI_SHOW_MENU(MENU_FILENUM, 1000, music_player_get_file_cur(), NULL);
    UI_MSG_POST("music_start:show_lyric=%4:dev=%4:filenum=%4:total_filenum=%4", !analaz, logo, music_player_get_file_cur(), music_player_get_file_total());
    ///smartbox info update
    SMARTBOX_UPDATE(MUSIC_FUNCTION_MASK,
                    BIT(MUSIC_INFO_ATTR_STATUS) | BIT(MUSIC_INFO_ATTR_FILE_NAME) | BIT(MUSIC_INFO_ATTR_FILE_PLAY_MODE));

}

//*----------------------------------------------------------------------------*/
/**@brief    music_player获取文件大小
   @param
   @return   文件大小, 单位:字节,-1:无效
   @note
*/
/*----------------------------------------------------------------------------*/
u32 music_player_get_file_fsize(void)
{
    if (__this && __this->file) {
        struct vfs_attr tmp_attr = {0};
        fget_attrs(__this->file, &tmp_attr);
        return tmp_attr.fsize;
    }
    return (u32) - 1;
}

三.解码基础接口

解码器接口函数基本在 music_player.h 文件找到说明,详细可以看注释
关键字:文件名后缀 文件格式

struct file_dec_hdl *dec = get_file_dec_hdl();
if (dec) {
    if (dec->file_dec.decoder.dec_ops->coding_type == AUDIO_CODING_APE
        || dec->file_dec.decoder.dec_ops->coding_type == AUDIO_CODING_FLAC
        || dec->file_dec.decoder.dec_ops->coding_type == AUDIO_CODING_DTS
        || dec->file_dec.decoder.dec_ops->coding_type == AUDIO_CODING_WAV) {
        
    }
}

四. 性能测试数据

1. 文件播放速度测试数据

AC696 U盘(2k多文件夹)--2021-7-7(单位:秒)AC696 U盘(1w单文件夹)-2021-7-7(单位:秒)
测试次数第一次第二次第三次测试次数第一次第二次第三次
首次上电读取4.484.5首次上电读取6.256.256.25
第―首歌曲断点拔插4.474.314.38第―首歌曲断点拔插6.266.25
最后―首歌曲断点拔插4.474.474.41最后―首歌曲断点拔插6.256.25
第―首歌上―曲0.360.360.36第―首歌上―曲0.430.430.43
第―首歌下―曲0.160.160.16第―首歌下―曲0.160.160.16
第五首连续上一曲0.40.40.4第五首连续上一曲0.360.380.36

2. 支持的音频格式

AC696、AC695

格式码率(单位kbps)采样率(单位Hz)位宽声道
aac/m5a8~5768k/11025/12k/16k/22050/24k/32k/44100/48k16bit 【解码出来的位宽】单/双声道
amr4.75/5.15/5.9/6.7/7.4/7.95/102/12.28k16bit 【解码出来的位宽】单声道
dts32~38408k/11025/12k/16k/22050/24k/32k/44100/48k16bit 【解码出来的位宽】5.1声道
alac1、无损格式一般压缩比为50%左右,编码好的文件都是vbr的,
要完成编码后才知道具体码率,各种码率解码器都可以支持,
速度瓶颈在读数据(设备读速度)
2、ape解码支持可支持Fast/Normal/High这3种,有的SDK速
度不够,可能会限制不支持High模式,使用Monkey's Audio
压缩工具编码High文件可测试。
8k/11025/12k/16k/22050/24k/32k/44100/48k16bit单/双声道
ape8k/11025/12k/16k/22050/24k/32k/44100/48k16bit单/双声道
flac8k/11025/12k/16k/22050/24k/32k/44100/48k16bit单/双声道
mp3全码率,支持layer1,layer2,layer38k/11025/12k/16k/22050/24k/32k/44100/48k16bit 【解码出来的位宽】单/双声道
wav码率有留个上限const变量WAV_MAX_BITRATEV给外部配置,
超过上限码率的返回不支持。例如 12bit2ch48k的pcm,
码率较高
【不支持3bit ima,alaw,ulaw等】
8k/11025/12k/16k/22050/24k/32k/44100/48kwav支持8/16/24/32bit的pcm,
4bit的ima跟4bit ms adpcm
单/双声道
wma支持Windows Media Audio中非加密的格式
【不支持Windows Media Audio Professional, Windows Media Audio Lossless,Windows Media Audio Voice,或者 加密的文件】
8k/11025/12k/16k/22050/24k/32k/44100/48k16bit 【解码出来的位宽】单/双声道
g7268k,16k支持4bit的解码,解码出来的位宽为16bit单声道
midi8k/11025/12k/16k/22050/24k/32k/44100/48k16bit 【解码出来的位宽】双声道

五. 功能

  1. 启动解码后但不马上开始播放
u8 file_dec_start_pause = 0;    // 启动解码后但不马上开始播放
  1. 无缝播放

2.1 无缝播放内置提示音

  • AC696/AC695 音箱SDK修改方法
  1. 在文件SDK\cpu\br25\audio_dec\audio_dec_tone.c 添加以下函数
/*----------------------------------------------------------------------------*/

/**@brief    内置提示音循环播放回调接口

   @param    *priv: 私有参数

   @return   0:循环播放

   @return   非0:结束循环

   @note

*/

/*----------------------------------------------------------------------------*/

static int tone_dec_repeat_cb(void *priv)

{

    struct tone_dec_handle *dec = priv;

    printf("file_dec_repeat_cb\\n");

    if (dec->repeat_num) {

        dec->repeat_num--;

    } else {

        y_printf("file_dec_repeat_cb end\\n");

        return -1;

    }

    return 0;

}

/*----------------------------------------------------------------------------*/

/**@brief    内置提示音设置循环播放次数

   @param    repeat_num: 循环次数

   @return   true:成功

   @return   false:失败

   @note

*/

/*----------------------------------------------------------------------------*/

int tone_dec_repeat_set(struct audio_dec_file_app_hdl * file_dec,u8 repeat_num)

{

    struct tone_dec_handle *dec = file_dec->priv;

    switch (file_dec->dec->decoder.dec_ops->coding_type) {

    case AUDIO_CODING_WAV: 

    case AUDIO_CODING_MP3: 

    {

        dec->repeat_num = repeat_num;

        struct audio_repeat_mode_param rep = {0};

        rep.flag = 1; //使能

        rep.headcut_frame = 2; //依据需求砍掉前面几帧,仅mp3格式有效

        rep.tailcut_frame = 2; //依据需求砍掉后面几帧,仅mp3格式有效

        rep.repeat_callback = tone_dec_repeat_cb;

        rep.callback_priv = dec;

        rep.repair_buf = &dec->repair_buf;

        audio_decoder_ioctrl(&(file_dec->dec->decoder), AUDIO_IOCTRL_CMD_REPEAT_PLAY, &rep);

    }

    return true;

    }

    return false;

}
  1. SDK\cpu\br25\audio_dec\audio_dec_tone.h 添加以下结构体参数
/*

 * 提示音解码结构体

 * 可以实现多个tone_dec_list_handle播放,通过结构体中的链表连接

 * 当解码中有正弦波数组播放时,必须要实现*get_sine接口

 */

struct tone_dec_handle {

    struct list_head head;	// 链表头

    struct audio_dec_sine_app_hdl *dec_sin;		// 文件播放句柄

    struct audio_dec_file_app_hdl *dec_file;	// sine播放句柄

    struct tone_dec_list_handle *cur_list;		// 当前播放list

    struct sin_param *(*get_sine)(u8 id, u8 *num);	// 按序列号获取sine数组

    OS_MUTEX mutex;		// 互斥

    u8 repeat_num;			// 无缝循环次数

    struct fixphase_repair_obj repair_buf;	// 无缝循环句柄
};
  1. 添加以下内容 SDK\cpu\br25\audio_dec\audio_dec_tone.c 的 tone_dec_file_app_evt_cb 函数
case AUDIO_DEC_APP_EVENT_START_INIT_OK:

        log_i("tone_file start init ok\\n");

        if (dec->cur_list->stream_handler) {

            // 删除原有的数据流,需要在回调中重新设置

            if (file_dec->dec->stream) {

                audio_stream_del_entry(&file_dec->dec->mix_ch.entry);

                audio_stream_del_entry(&file_dec->dec->decoder.entry);

                audio_stream_close(file_dec->dec->stream);

                file_dec->dec->stream = NULL;

            }

            dec->cur_list->stream_handler(dec->cur_list->stream_priv, event, file_dec->dec);

        }

        clock_add_set(DEC_TONE_CLK);

        audio_dec_file_app_init_ok(file_dec);

        tone_dec_repeat_set(file_dec,3);//这次传重复次数,如果需要一直循环,需回调函数一直返回0
        break;
  1. 测试函数
case KEY_TONE_REPLAY_TEST:

        {

            extern int tone_play_repeat(const char *name, u8 preemption);

            tone_play_by_path(tone_table[IDEX_TONE_BLE_DS], 1);

        }

        break;
  • 注意点: 无缝播放只支持 MP3、WAV格式
  1. 按文件号播放 我们文件号取决于文件放进文件系统中的时间,从早到晚依次排序
//*----------------------------------------------------------------------------*/
/**@brief    music_player序号播放指定设备
   @param
                logo:逻辑盘符,如:sd0/sd1/udisk0
                number:指定播放序号
   @return   播放错误码
   @note
*/
/*----------------------------------------------------------------------------*/
int music_player_play_by_number(char *logo, u32 number)
  1. 按路径播放
  • 短文件名/文件夹播放(不支持长文件名:超过8个字节文件名一般为长文件名,长文件夹名字也不支持)

  • 长文件名(只支持完整路径播放)

  • 中文路径 程序识别中文是使用ANSI编码格式,
    要使用中文路径,需知中文路径名字在ANSI编码的16进制
    方法:使用记事本设置编码格式:
    image 然后使用WINHEX打开,16进制为:
    image 字符串:char zhongwen[6] = {​0xD6,0xD0,0xCE,0xC4​};

使用方法:
比如:storage/udisk0/C/中文.txt 这个路径

char zhongwen[6] = {0xD6,0xD0,0xCE,0xC4};
char path[80] = {0}
sprintf(path, "%s%s%s", "storage/udisk0/C/", zhongwen,".txt");   
printf("name == %s\n",path);                       //path == ”storage/udisk0/C/中文.txt”                       
  1. 指定文件夹播放
  • 实现原理:重新指定一个扫描路径去获取到指定路径的,然后根据文件号进行播放
  • 步骤:
  1. apps\common\dev_manager\dev_manager.c 添加以下函数
//*----------------------------------------------------------------------------*/
/**@brief   设备扫盘
   @param
               dev:设备节点
               path:指定扫描目录
               parm:扫描参数,包括文件后缀等
               cycle_mode:播放循环模式
            callback:扫描打断回调
   @return  成功返回扫描控制句柄,失败返回NULL
   @note
*/
/*----------------------------------------------------------------------------*/
struct vfscan *dev_manager_scan_disk_by_user(struct __dev *dev, const char *path, const char *parm, u8 cycle_mode, struct __scan_callback *callback)
{
    if (dev_manager_check(dev) == NULL) {
        return NULL;
    }
#if TCFG_USB_DM_MULTIPLEX_WITH_SD_DAT0

#if (TCFG_DM_MULTIPLEX_WITH_SD_PORT == 0)     //0:sd0  1:sd1 //dm 参与复用的sd配置
        if(!memcmp(dev->parm->logo ,"sd0",strlen("sd0"))){
#else
        if(!memcmp(dev->parm->logo ,"sd1",strlen("sd1"))){
#endif
            dev_usb_change_sd();
        }

    if(!memcmp(dev->parm->logo ,"udisk",strlen("udisk")))
        dev_sd_change_usb();

    if (dev_manager_online_check(dev, 1) == NULL) {
        printf("mult remount fail !!!\n");
        return NULL;
    }
#endif
    char *fsn_path = NULL;
    char *tmp_path = NULL;
    if (path) {
        if (*path == '/') {
            path++;
        }
        tmp_path = zalloc(strlen(dev->parm->root_path) + strlen(path) + 1);
        if (tmp_path == NULL) {
            return NULL;
        }
        sprintf(tmp_path, "%s%s", dev->parm->root_path, path);
        fsn_path = tmp_path;
    } else {
        fsn_path = dev->parm->root_path;
    }
    printf("fsn_path = %s, scan parm = %s\n", fsn_path, parm);
    struct vfscan *fsn;
    /* clock_add_set(SCAN_DISK_CLK); */
    if(callback && callback->enter){
        callback->enter(dev);//扫描前处理, 可以在注册的回调里提高系统时钟等处理
    }
    fsn = fscan_interrupt(
            (const char *)fsn_path,
            parm,
            DEV_MANAGER_SCAN_DISK_MAX_DEEPTH,
            ((callback) ? callback->scan_break : NULL));
    /* clock_remove_set(SCAN_DISK_CLK); */
    if(callback && callback->exit){
        callback->exit(dev);//扫描后处理, 可以在注册的回调里还原到enter前的状态
    }
    y_printf("fsn->file_number %d\n",fsn->file_number);
    if (fsn) {
        if (fsn->file_number == 0) {
            printf("dev nofile\n");
#if (TCFG_DEV_UPDATE_IF_NOFILE_ENABLE)
            ///没有文件找升级文件
            dev_update_check(dev->parm->logo);
#endif/*TCFG_DEV_UPDATE_IF_NOFILE_ENABLE*/
            ///没有文件,释放fsn, 减少外面流程的处理
            dev_manager_scan_disk_release(fsn);
            fsn = NULL;
        } else {
            fsn->cycle_mode = cycle_mode;
        }
    }

    if (tmp_path) {
        free(tmp_path);
    }
    return fsn;
}
  1. apps\common\music\music_player.c 添加 int music_player_play_by_path_index(char *logo, const char *path,int index)
//*----------------------------------------------------------------------------*/
/**@brief    music_player路径播放指定设备
   @param
                logo:逻辑盘符,如:sd0/sd1/udisk0, 设置为NULL,为默认当前播放设备
                path:指定播放文件夹路径(例如:/dir2)
                index:文件夹内的序号(1~N)
   @return   播放错误码
   @note
*/
/*----------------------------------------------------------------------------*/
int music_player_play_by_path_index(char *logo, const char *path,int index)
{
    static struct vfscan   *fsn;//设备扫描句柄
    if (path == NULL) {
        return MUSIC_PLAYER_ERR_POINT;
    }
    if (logo == NULL) {
        music_player_stop(0);
        if (dev_manager_online_check(__this->dev, 1) == 0) {
            return MUSIC_PLAYER_ERR_DEV_OFFLINE;
        }
        ///没有指定设备不需要找设备, 不需要扫描
    } else {
        music_player_stop(0);
        __this->dev = dev_manager_find_spec(logo, 1);
        if (__this->dev == NULL) {
            return MUSIC_PLAYER_ERR_DEV_NOFOUND;
        }
        fsn = dev_manager_scan_disk_by_user(__this->dev, path, scan_parm, app_var.cycle_mode, __this->parm.scan_cb);
    }
    if (fsn == NULL) {
        return MUSIC_PLAYER_ERR_FSCAN;
    }
    y_printf("index %d\n",index);
    ///get file
    __this->file = file_manager_select(__this->dev, fsn, FSEL_BY_NUMBER, index, __this->parm.scan_cb);//根据簇号查找文件
    if (__this->file == NULL) {
        return MUSIC_PLAYER_ERR_FILE_NOFOUND;
    }
    // //释放fsn, 减少外面流程的处理
    dev_manager_scan_disk_release(fsn);
    ///start decoder
    int err = music_player_decode_start(__this->file, 0);
    if (err == MUSIC_PLAYER_SUCC) {
        ///选定新设备播放成功后,需要激活当前设备
        dev_manager_set_active(__this->dev);
        log_i("[%s %d] ok\n", __FUNCTION__, __LINE__);
    }
    return err;
}

  • 使用方法:
music_player_play_by_path_index((char *)"udisk0", "/dir2",1);///this is a demo
  1. 文件浏览 可以参考 SDK\apps\common\file_operate\file_bs_deal.c 中的void file_bs_test(void
void file_bs_test(void)
  1. IR按键实现选择第X首歌曲播放 image
/*********************************************************************************************************//**
  * @brief  IR数字按键播放音乐
  * @retval 
  ***********************************************************************************************************/
static u16 auto_time_id = 0;
void user_music_play_finle_number(void *priv){
    int *filenum = (int *)priv;
    auto_time_id = 0;
    y_f_printf(">>>number %d %d\n",*filenum);
    if(*filenum)
    {
        music_player_play_by_number(music_player_get_dev_cur(),*filenum);
        // music_play_msg_post(3, MPLY_MSG_FILE_BY_DEV_FILENUM, (int)music_play_get_cur_dev(), *filenum);
    }
    *filenum = 0;
    y_f_printf(">>>>%d\n",*filenum);
}
//设置MUSIC音量
void user_music_set_file_number(int number)
{
    #if USER_IR_PLAY_FILE_NUMBER
    static int filenum = 0;
    s32 tp = filenum*10+number;
    y_f_printf(">>>>>music_play_get_file_total_file %d\n",music_player_get_file_total());
    if(auto_time_id){
        sys_timeout_del(auto_time_id);
    }
    if((tp>music_player_get_file_total()) ||  tp/10000){
        filenum = 0;
        UI_SHOW_MENU(MENU_SET_ERR, 3000, 0, NULL);
        return;
    }
    else{
        filenum = tp;
    }
    y_f_printf(">>>>> filenum %d\n",filenum);
    UI_SHOW_MENU(MENU_FILENUM, 4000, filenum, NULL);
    if(filenum>1000){
        user_music_play_finle_number(&filenum);
        filenum = 0;
    }
    else{
        auto_time_id = sys_timeout_add(&filenum,user_music_play_finle_number,3000);
    }
    #endif
}

FAQ

  1. AC695X 3.1.0SDKmusic下切设备顺序以及是否播放设备提示音------------SQ 修改文件包
  2. 程序msuic.c修改
    • 添加宏定义、变量
/***添加宏定义、变量 start***/
#define USER_DEV_USB    "udisk0"
#define USER_DEV_SD0    "sd0"
#define USER_DEV_SD1    "sd1"
u8 dev_order=1;//0:默认sdk 1:usb优先 2:sd卡优先
u8 dev_tone=0;//0:不播放设备提示音 1:播放设备提示音
u8 u_dev_mount_flag=0;//设备上标志 1 usb 2 sd
/***添加宏定义、变量 end***/
- 设备提示音使能
///设备提示音使能
#define MUSIC_DEVICE_TONE_EN                1
- 提示音名称使其与外部工具配置一致
const struct __dev_tone device_tone[] = {
{"udisk0",      "udisk0",   TONE_RES_ROOT_PATH"tone/usb.*"/**名称与外部配置工具保持一致**/, DEVICE_INDEX_UDISK},
{"udisk0_rec",  "udisk0",   TONE_RES_ROOT_PATH"tone/usb.*"/**名称与外部配置工具保持一致**/, DEVICE_INDEX_UDISK_REC},
{"sd0",         "sd0",      TONE_RES_ROOT_PATH"tone/sd.*"/**名称与外部配置工具保持一致**/,  DEVICE_INDEX_SD0},
{"sd0_rec",     "sd0",      TONE_RES_ROOT_PATH"tone/sd.*"/**名称与外部配置工具保持一致**/,  DEVICE_INDEX_SD0_REC},
{"sd1",         "sd1",      TONE_RES_ROOT_PATH"tone/sd.*"/**名称与外部配置工具保持一致**/,  DEVICE_INDEX_SD1},
{"sd1_rec",     "sd1",      TONE_RES_ROOT_PATH"tone/sd.*"/**名称与外部配置工具保持一致**/,  DEVICE_INDEX_SD1_REC},
};
- 嵌入控制设备提示音播放变量
        if (strcmp(device_tone[i].logo, logo) == 0) {
            log_i("[%s, %d]device_tone play \n", logo, __LINE__);
            memset(__this->device_tone_dev, 0, sizeof(__this->device_tone_dev));
            memcpy(__this->device_tone_dev, device_tone[i].phy_logo, strlen(device_tone[i].phy_logo));
            /***嵌入控制设备提示音播放变量 start**/
            if(dev_tone){
                tone_play_with_callback_by_name(device_tone[i].tone_path, 1, music_tone_play_end_callback, (void *)device_tone[i].index);
            }else{
                music_tone_play_end_callback((void *)device_tone[i].index,0);
            }
            /***嵌入控制设备提示音播放变量 end**/
            return true;
        }
- KEY_MUSIC_PLAYER_START消息处理修改
#if (MUSIC_DEVICE_TONE_EN)
        if (/*start*/dev_tone &&/*end*/ (music_device_tone_play(logo) == true)) {
            UI_SHOW_MENU(MENU_MUSIC_SEITCH_DEV, 0, 0, NULL);
            break;
        }
#endif
- 添加KEY_CHANGE_MODE消息处理
    case KEY_CHANGE_MODE:
        r_printf("KEY_CHANGE_MODE");
        if((dev_manager_get_total(1)>1)){
            char *active_by_logo=NULL;
            if(dev_order){
                if(1==dev_order){
                    active_by_logo=USER_DEV_USB;
                }else if(2==dev_order){
                    #if TCFG_SD0_ENABLE
                    active_by_logo=USER_DEV_SD0;
                    #elif TCFG_SD1_ENABLE
                    active_by_logo=USER_DEV_SD1;
                    #endif
                }
                if(active_by_logo && (music_player_get_dev_cur()==active_by_logo)){
                    app_task_put_key_msg(KEY_MUSIC_AUTO_NEXT_DEV,0);
                    break;
                }
            }
        }
        ret = false;
        break;
- 进music模式添加播放逻辑处理
void app_music_task()
{
    int res;
    int msg[32];
    music_task_start();

    /***多设备在线判断 start***/
    if((dev_manager_get_total(1)>1)){
        char *active_by_logo=NULL;
        if(dev_order){
            if(1==dev_order){
                if(2==u_dev_mount_flag){
                    #if TCFG_SD0_ENABLE
                    active_by_logo=USER_DEV_SD0;
                    #elif TCFG_SD1_ENABLE
                    active_by_logo=USER_DEV_SD1;
                    #endif
                }else{
                    active_by_logo=USER_DEV_USB;
                }
            }else if(2==dev_order){
                if(1==u_dev_mount_flag){
                    active_by_logo=USER_DEV_USB;
                }else{
                    #if TCFG_SD0_ENABLE
                    active_by_logo=USER_DEV_SD0;
                    #elif TCFG_SD1_ENABLE
                    active_by_logo=USER_DEV_SD1;
                    #endif
                }
            }
            dev_manager_set_active_by_logo(active_by_logo);
        }
    }

    /***多设备在线判断 end***/

#if (MUSIC_DEVICE_TONE_EN)
    
    /***是否播放设备提示音 start***/
    if (dev_tone){
        music_player_play_start();
    }else{
        tone_play_with_callback_by_name(tone_table[IDEX_TONE_MUSIC], 1, music_tone_play_end_callback, (void *)IDEX_TONE_MUSIC);
    }
    /***是否播放设备提示音 end***/
#else
    .....
    .....
  1. app_common.c修改内容
    • 设备上线标记
    case DRIVER_EVENT_FROM_SD0:
    case DRIVER_EVENT_FROM_SD1:
    case DRIVER_EVENT_FROM_SD2:
#if TCFG_APP_MUSIC_EN
        ret = dev_status_event_filter(event);///解码设备上下线, 设备挂载等处理
        if (ret == true) {
            if (event->u.dev.event == DEVICE_EVENT_IN) {
                ///设备上线, 非解码模式切换到解码模式播放
                if (app_get_curr_task() != APP_MUSIC_TASK) {
                    app = APP_MUSIC_TASK;
                    /*设备上线标记 satrt*/
                    extern u8 u_dev_mount_flag;//1 usb 2 sd
                    if(((u32)event->arg)==DRIVER_EVENT_FROM_SD0 || ((u32)event->arg)==DRIVER_EVENT_FROM_SD2){
                        u_dev_mount_flag=2;
                    }else{
                        u_dev_mount_flag=1;
                    }
                    /*设备上线标记 end*/
                }
            }
        }
#endif
  1. 播放设备提示音的地方需要主要提示音名称一定要在工具里面有 image
  2. 如果不需要分开播放设备提示音就把提示音改为一样,同样需要满足第3点 image

5.播放隐藏文件

image

  1. 696 170 删除设备中的文件夹 内部git
    ac696x_soundbox_sdk-1.7.0-删除设备中的文件夹.7z

本文档包含以下子页面

Prev
BLE测试数据
Next
MIDI音乐实现