找回密码
 立即注册
楼主: health

MP3解码播放流畅@Ai8051U核心板-擎天柱,8051U移植minimp3解码库

[复制链接]
  • 打卡等级:常住居民III
  • 打卡总天数:165
  • 最近打卡:2026-03-07 00:57:19

25

主题

231

回帖

2033

积分

金牌会员

积分
2033
发表于 2026-1-8 12:19:56 | 显示全部楼层
再有一个就是加入饱和指令,包括浮点数转整数,长整数转短整数。

处理器指令中的饱和指令(Saturation Instructions)是什么?
饱和指令(Saturation Instructions) 是处理器(尤其是数字信号处理器(DSP)、多媒体扩展指令集(如MMX/SSE/Neon) 和图形处理器(GPU))中的一类特殊算术指令。其核心功能是:当计算结果超出目标数据类型能表示的范围时,不是进行标准的“环绕”(Wrap Around)或“截断”(Truncation),而是将结果“钳位”(Clamp)到该数据类型允许的最大值或最小值。

回复

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:165
  • 最近打卡:2026-03-07 00:57:19

25

主题

231

回帖

2033

积分

金牌会员

积分
2033
发表于 2026-1-22 02:17:49 | 显示全部楼层
修改一条语句,速度提升百倍。

51架构,移位是个短板,对于性能有要求的场合,多位的移位尽量用其它操作代替。
在minimp3源码的霍夫曼算法中,有一行对51极其不友好。
*dst = g_pow43[16 + lsb - 16*(bs_cache >> 31)] * one;
bs_cache是uint32类型,对其右移31位。
51架构指令每次只能移位一位,多位移位就需循环重复多次处理。这里的 (>>31) 就需要循环执行31次 (>>1)。
另外这个变量还是32位类型,51及251不能直接移位32位值,需要分成高低字节分别移位,同时要把高字节移出的位移入低字节。再加上移位数的判断,C251下移位移位至少需要8~9个时钟,31位移完需要二三百个时钟。

一般移位数量越多耗时越长,移31位是最苛刻的情况了,移位超过31位就轻松多了。
另外,移位8位,16位,24位这种也是比较快的,移位它们可以直接用字节读写代替。

对于32位变量,左移也要比右移轻松一些。
左移一位 (<<1)等效于 乘以2,可以用加法指令一个时钟完成,例如 ADD DR0, DR0。

总之一句话,右移31位这个操作对于C51和C251来说就是顶级难度了。

回过头再看源码,(bs_cache >> 31)只是为了取出最高位即符号位的值,最高位为1结果即为1,最高位0结果即为0。
花费数百个时钟周期,只为取符号位的值,是不是有点太费周章了。
用与操作改写上面语句为:*dst = g_pow43[16 + lsb - ((bs_cache & 0x80000000) ? 16 : 0)] * one; 速度得到极大提升。

上面仍不完美,32位变量的与操作仍不被C51/C251指令硬件支持,需要多条指令完成。后面还有一个判断跳转分支指令,跳转指令因为打乱流水线,也是需要多消耗周期的。
最终修改如下:
*dst = g_pow43[16 + lsb - ((((uint8_t)(bs_cache>>24)) >> 3)&0x10)] * one;

点评

楼主威武,新知识+1  发表于 2026-2-5 21:12
回复

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:165
  • 最近打卡:2026-03-07 00:57:19

25

主题

231

回帖

2033

积分

金牌会员

积分
2033
发表于 2026-2-5 14:19:42 | 显示全部楼层
static float L3_ldexp_q2(float y, int exp_q2)
{
    static const float g_expfrac[4] = { 9.31322575e-10f, 7.83145814e-10f, 6.58544508e-10f, 5.53767716e-10f };
    int e;
       
    do
    {
        e = MINIMP3_MIN(30*4, exp_q2);
        y *= g_expfrac[e & 3] * tab[e >> 2];
               
    } while ((exp_q2 -= e) > 0);
       
    return y;
}

修改为

static float L3_ldexp_q2(float y, int exp_q2)
{
    static const float g_expfrac[4] = { 9.31322575e-10f, 7.83145814e-10f, 6.58544508e-10f, 5.53767716e-10f };
    static const float tab[31] = {0x40000000, 0x20000000, 0x10000000, 0x08000000, 0x04000000, 0x02000000, 0x01000000, 0x00800000,
                                            0x00400000, 0x00200000, 0x00100000, 0x00080000, 0x00040000, 0x00020000, 0x00010000, 0x00008000,
                                            0x00004000, 0x00002000, 0x00001000, 0x00000800, 0x00000400, 0x00000200, 0x00000100, 0x00000080,
                                            0x00000040, 0x00000020, 0x00000010, 0x00000008, 0x00000004, 0x00000002, 0x00000001};
    int e;
       
    do
    {
        e = MINIMP3_MIN(30*4, exp_q2);
               
        //y *= g_expfrac[e & 3] * (1UL << 30 >> (e >> 2));
        y *= g_expfrac[e & 3] * tab[e >> 2];
               
    } while ((exp_q2 -= e) > 0);
       
    return y;
}

32位整数移位,以及32位整数转float,统一使用查表获得,代价是代码多占用31*4字节。
回复

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:165
  • 最近打卡:2026-03-07 00:57:19

25

主题

231

回帖

2033

积分

金牌会员

积分
2033
发表于 2026-2-7 17:32:01 | 显示全部楼层
(续上)
浮点数相乘,乘数为0x01移位所得,即都是2的n次方。
浮点数乘以2^n,等效为被乘浮点数的指数+n。
因此,将一次浮点乘运算简化为int8加运算。

float L3_ldexp_q2(float y, int exp_q2)
{
    static const float g_expfrac[4] = { 9.31322575e-10f, 7.83145814e-10f, 6.58544508e-10f, 5.53767716e-10f };

    int e;
    uint16_t exp;
    float f;
       
    do
    {
        e = MINIMP3_MIN(30*4, exp_q2);
        f = g_expfrac[e & 3];
        exp = (uint16_t)((int8_t)(((*(uint16_t *)&f) << 1) >> 8) + (int8_t)(30-(e>>2))) << 7;
        *(uint16_t *)&f = *(uint16_t *)&f & 0x807F | exp;       
        y *= f;               
    } while ((exp_q2 -= e) > 0);
    return y;
}
回复

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:165
  • 最近打卡:2026-03-07 00:57:19

25

主题

231

回帖

2033

积分

金牌会员

积分
2033
发表于 2026-2-9 19:24:05 | 显示全部楼层
(续2)
上面采用移位操作,每次最多只能移位30位,大于30的数值需要多次循环逐步完成。
使用指数加减,无此限制,无需循环,一次完成。
再次精简为
float L3_ldexp_q2(float y, int16_t exp_q2)
{
    static const float g_expfrac[4] = {1.0, 0.84089642, 0.70710678, 0.59460355};
    float f;   
    uint16_t f_exp;
       
    f = g_expfrac[exp_q2 & 3];
    f_exp = (((((*(uint16_t *)&f) << 1) >> 8) - (exp_q2 >> 2)) << 8) >> 1;
    *(uint16_t *)&f = *(uint16_t *)&f & 0x807F | f_exp;
    y *= f;
    return y;
}

回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-3-7 17:04 , Processed in 0.107862 second(s), 58 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表