耳机网-耳机大家坛

标题: 终于从数学原理上明白为什么SACD和CD出来的声音不一样和DSD的电平比较低了 [打印本页]

作者: zasflower    时间: 2023-4-13 20:59
标题: 终于从数学原理上明白为什么SACD和CD出来的声音不一样和DSD的电平比较低了
本帖最后由 zasflower 于 2023-4-13 23:36 编辑

最近在优化DSD解码的代码的cpu性能,突然想到了为什么DSD的声音不一样了。实际上你把CD转成DSD64,然后再用DSD低通滤波器重新滤波再转成CD,声音也是不一样的。

数学原理就是这样,DSD-PCM PCM-DSD转换不是无损的。这和ape转flac或者flac转wav不一样。实际上采用什么滤波器和滤波器截止频率选取在那个点声音都会不一样。

代码是这样子:
//dsf DSD file(SACD)
                public function decodeDsfDSD(dsdBytes:ByteArray,sampleEventOutBytes:ByteArray):void
                {
                        var byte:uint ;
                        var dataLeft:Number;
                        var dataRight:Number;
                        var readInt:uint;
                        var indexL:int=0;
                        var indexR:int =0;
                        
                        if(dsdBytes.position==60){
                                dsdBytes.position = 92;
                        }
                        
                        for (var i:int = 0; i < 8192; ) {
                                for(var mny:int=0;mny<512;mny++){
                                        dataLeft = 0;
                                        for(var m:int=0;m<2;m++){
                                                readInt = dsdBytes.readUnsignedInt();
                                                byte = readInt >> 24 & 0xFF;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   1  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   2  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   3  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   4  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   5  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   6  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   7  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                byte = readInt >> 16 & 0xFF;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   1  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   2  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   3  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   4  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   5  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   6  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   7  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                byte = readInt >> 8 & 0xFF;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   1  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   2  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   3  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   4  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   5  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   6  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   7  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                byte = readInt & 0xFF;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   1  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   2  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   3  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   4  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   5  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   6  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                                lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   7  ) & 0x01)*2-1 - lpfdataL );
                                                lpfdata2L = lpfdata2L+ cacheAlpha*(lpfdataL - lpfdata2L );
                                                dataLeft += lpfdata2L;
                                        }
                                        i++;
                                        // +6.0db SACD
                                        // if +0db set to dataLeft/64
                                        // 64bit
                                        samplesL[indexL]=dataLeft/32;
                                        indexL++;
                                }
                                i -= 512;
                                for(var mnk:int=0;mnk<512;mnk++){
                                        dataRight = 0;
                                        for( m=0;m<2;m++){
                                                readInt = dsdBytes.readUnsignedInt();
                                                byte = readInt >> 24 & 0xFF;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   1  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   2  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   3  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   4  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   5  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   6  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   7  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                byte = readInt >> 16 & 0xFF;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   1  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   2  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   3  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   4  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   5  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   6  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   7  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                byte = readInt >> 8 & 0xFF;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   1  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   2  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   3  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   4  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   5  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   6  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   7  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                byte = readInt & 0xFF;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   1  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   2  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   3  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   4  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   5  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   6  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                                lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   7  ) & 0x01)*2-1 - lpfdataR );
                                                lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
                                                dataRight += lpfdata2R;
                                        }
                                        i++;
                                        // +6.0db SACD
                                        // if +0db set to dataRight/64
                                        // 64bit
                                        samplesR[indexR]=dataRight/32;
                                        indexR++;
                                }
                        }
                        //need write data later
                        for(i=0;i<8192;i++){
                                // 32bit
                                sampleEventOutBytes.writeFloat(samplesL);
                                sampleEventOutBytes.writeFloat(samplesR);
                        }
                }



主要公式是这个:
lpfdataL = lpfdataL+ cacheAlpha*((( byte >>   7  ) & 0x01)*2-1 - lpfdataL )

将二进制右移动7位检查当前1bit二进制是0还是1,0就是-1.0电平 1就是+1.0电平。

然后输出 lpfdataL  会加上高频的目标电平。会缓慢逼近目标电平。相当于 y += (dsd-y) * cacheAlpha
cacheAlpha 取值为0-0.05-0.2-0.5-0.7-1,0则会切除所有频率。1不会动任何频率。(取中间则数则切除DSD高频噪声)

因为在逼近目标电平+1.0 -1.0(因为要切除高频噪声所以cacheAlpha不可能取值1,不然任何频率不会动) 所以DSD出来的声音默认就是比较小的。




作者: 大黄狗将军    时间: 2023-4-13 21:45
技术大佬,我们都是平民玩家,直接说结果
作者: zasflower    时间: 2023-4-13 21:47
本帖最后由 zasflower 于 2023-4-13 22:34 编辑
大黄狗将军 发表于 2023-4-13 21:45
技术大佬,我们都是平民玩家,直接说结果

DSD 最终输出的电平比CD低一半左右。6db

然后不同的低通滤波器会是不同的声音,音色不同。我自己手撸了一个音乐播放器用来放SACD,换一个低通滤波器出来的声音都不一样。其它环境变量都一样,就是滤波器代码和算法参数不一样。



作者: kevin_dp    时间: 2023-4-13 22:35
zasflower 发表于 2023-4-13 21:47
DSD 最终输出的电平比CD低一半左右。6db

然后不同的低通滤波器会是不同的声音,音色不同。我自己手撸 ...

因为电平低所以听起来更柔和?
作者: zasflower    时间: 2023-4-13 22:37
本帖最后由 zasflower 于 2023-4-13 22:38 编辑
kevin_dp 发表于 2023-4-13 22:35
因为电平低所以听起来更柔和?

不是,滤波器的原因。

你看那个代码应该能理解。
做过电路板的就知道那个一阶滤波器的意思。
一个乘法器,一个加法器。


DSD的高频听起来比较柔和,没有CD那么冲。

作者: 当然是反二啦    时间: 2023-4-13 23:12
再次说明了玩滤波跟别的比是数字音频时代更正确的选择(自我骄傲
作者: prodomo    时间: 2023-4-13 23:28
电平低就会显得柔和,不同版本的CD/pcm差距就很大,另外有些再版厂牌也会把声音做柔和一点。这样声音兼容度好一点,不然低端CD机听原版会刺激。器材好的话就不怕原版的刺激。
作者: 番茄炒蛋饭    时间: 2023-4-13 23:58
DSD是噪音整形之后动态范围变小 +-0.5 而PCM是+-1
作者: zasflower    时间: 2023-4-14 04:19
本帖最后由 zasflower 于 2023-4-14 04:25 编辑

byte = readInt  & 0xFF;
lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   7  ) & 0x01)*2-1 - lpfdataR );
lpfdata2R = lpfdata2R+ cacheAlpha*(lpfdataR - lpfdata2R );
lpfdata3R = lpfdata3R+ cacheAlpha*(lpfdata2R - lpfdata3R );
lpfdata4R = lpfdata4R+ cacheAlpha*(lpfdata3R - lpfdata4R );
dataRight += lpfdata4R;

这个是一个4次的低通滤波器。而这个滤波器的公式就来自于DSD直通模拟滤波器的电路板原理。
lpfdataR = lpfdataR+ cacheAlpha*((( byte >>   7  ) & 0x01)*2-1 - lpfdataR );

DSD SACD 经过多阶的滤波后确实声音要安定很多,高频柔和不冲了。实际上可以进行更多阶数的低通滤波器,只要CPU DSP能撑住。

但是原理是类似的,就是滤波器造成的。
作者: zasflower    时间: 2023-4-14 04:43
本帖最后由 zasflower 于 2023-4-14 07:39 编辑

https://github.com/sulinhuang/KMusicPlayerWindows/releases/tag/v171Release

你可以拿这个试一下。KMusicPlayerWindows-v1.7.1-RC2.rar 按理我这个播放器后端仍然是使用PCM来播放,所以这个仍然是PCM的声音。但是用CPU滤波器处理后,
声音仍然是DSD那种声音安定和高频柔和的声音。

使用PCM系统播放DSD仍然会有模拟味,声音和高频很安定的感觉。即使这个DSD文件是由CD转换过来。


设定成这样。

DSD的文件即使在PCM系统播放仍然能听出和CD的音色区别。

按照SONY大法的说法,DSD要模拟电路直接模拟低通滤波器。但是用cpu来模拟那个模拟电路算法之后即使播放系统是PCM的,仍然和CD有区别。

现在像 Playback Designs 这类顶级DSD播放器也是用FPGA可程序化芯片来自行设计DSD算法。

所以我想来想去,就是DSD低通滤波器和程序算法造成的。
作者: RacingAK47    时间: 2023-4-14 04:52

作者: zasflower    时间: 2023-4-14 06:56
本帖最后由 zasflower 于 2023-4-14 07:39 编辑

我今天本来在测试代码优化后dsf文件播放有没有问题。然后手上刚好又没有dsf文件,就用TASCAM Hi-Res Editor随手转了一首我常听的wav为SACD的dsf。

然后播放出来声音和CD很明显不同。

按理来说这个dsd是由CD转过来的。播放也是用PCM系统播放。耳机电脑什么的都一样。

所以我后来仔细想了想,唯一的变量就是滤波器,就是DSD那个模拟滤波器造成的模拟味。

同样的CD的音源转换成DSD,然后用DSD模拟滤波器播放出来的声音即使在PCM回放系统声音也是DSD味道的。

就是那种高频柔和,声音不冲的DSD模拟味。

既然整个系统唯一的变量就是DSD的滤波器,所以我觉得DSD模拟味的来源就是低通滤波器。不管这个低通滤波器是用电路板用电阻实现还是cpu模拟出来的。它底层数学原理就会有模拟味道。

如果在PCM系统播放DSD也带模拟味能和CD听出很明显的区别(就是声音很安定,很柔和)。那这个模拟味就是来源于低通滤波器。不管它是电路板实现这个滤波器还是cpu模拟这个滤波器(我是用软件CPU模拟的)。像 Playback Designs 这类顶级DSD播放器也是用FPGA可程序化芯片来自行设计DSD回放算法。FPGA 其实就是弱化版本的CPU。
作者: stonerose    时间: 2023-4-14 07:18
学习了
作者: 番茄炒蛋饭    时间: 2023-4-14 07:40
不同意楼主观点 楼主代码是DSD转PCM 如果在DSD直通情况下 是直接DSD转模拟信号 然后低通模拟滤波器,40/50K 滤波 然后给运放
作者: handsdady    时间: 2023-4-14 08:07
hqplayer是不是就是楼主说的这种硬件处理滤波  让声音有各种变化?
作者: zasflower    时间: 2023-4-14 08:20
番茄炒蛋饭 发表于 2023-4-14 07:40
不同意楼主观点 楼主代码是DSD转PCM 如果在DSD直通情况下 是直接DSD转模拟信号 然后低通模拟滤波器,40/50K ...

实践是检验真理的唯一标准。
https://github.com/sulinhuang/KMusicPlayerWindows/releases/tag/v171Release

这个你下载下来。然后解压KMusicPlayerWindows-v1.7.1-RC2.rar

播放器先启动设置成这样。




然后选一首DSD播放,看PCM系统是不是出现了模拟味道。

就是那种DSD特别的背景和声音很安定,声音很柔和的味道。




作者: zasflower    时间: 2023-4-14 08:21
本帖最后由 zasflower 于 2023-4-14 08:29 编辑
handsdady 发表于 2023-4-14 08:07
hqplayer是不是就是楼主说的这种硬件处理滤波  让声音有各种变化?

有可能,具体没有软件源代码不清楚。

不过算法确实是可以改变声音回放风格的。Playback Designs也是用软件算法改变DSD回放声音风格。DSD格式就是这个大师发明的,只是他是写在FPGA里面的,FPGA就是个mini CPU.
作者: 耳机俱乐部小黑    时间: 2023-4-14 08:29
楼主是电脑模拟支出吗?
作者: zasflower    时间: 2023-4-14 08:30
本帖最后由 zasflower 于 2023-4-14 08:38 编辑
耳机俱乐部小黑 发表于 2023-4-14 08:29
楼主是电脑模拟支出吗?

是用CPU模拟DSD那个模拟低通滤波器。

这个是PCM系统的。因为我只能在PCM系统编辑处理声音。只是运行在64bit 2.8mhz这个虚拟频率。DSP系统运行在这个的虚拟PCM空间。

最终输出32bit 44.1khz PCM.
作者: abccbaa    时间: 2023-4-14 08:32
这个资料好


作者: pinkebinem    时间: 2023-4-14 08:37
不明觉厉
作者: 番茄炒蛋饭    时间: 2023-4-14 08:42
zasflower 发表于 2023-4-14 08:20
实践是检验真理的唯一标准。
https://github.com/sulinhuang/KMusicPlayerWindows/releases/tag/v171Rel ...

声音柔和是高频被衰减 动态范围下降导致的 主要是噪音整形带来的动态范围从-1..+1降低为-0.5..+0.5导致的 同时DSD转PCM算法要过低通,在DAC内部再过低通 多次低通滤波器对高频损失不小,所有听起来更柔和
作者: zasflower    时间: 2023-4-14 08:54
本帖最后由 zasflower 于 2023-4-14 08:56 编辑
番茄炒蛋饭 发表于 2023-4-14 08:42
声音柔和是高频被衰减 动态范围下降导致的 主要是噪音整形带来的动态范围从-1..+1降低为-0.5..+0.5导致的 ...

DSD动态没有下降的,只是电平下降。电平高低不决定动态,精度才是。电平只决定回放的音量。16bit 24bit 才决定动态。DSD电平低是低通滤波必然导致的。因为直通模拟电路就是一个跟随函数。一个电阻乘法器和串联加法器,所以如果是DSD直出,电平必然低6db,也就是音量为一半。而且不能调。
至于DSD这种声音风格有人喜欢有人不喜欢。

DSD电平低不会影响它的动态。只是DSD声音风格不同。

作者: 番茄炒蛋饭    时间: 2023-4-14 08:56
zasflower 发表于 2023-4-14 08:54
DSD动态没有下降的,只是电平下降。电平高低不决定动态,精度才是。电平只决定回放的音量。16bit 24bit  ...

你如果看过DSD的频谱就能明白原因了
作者: leonbernieni    时间: 2023-4-14 08:57
滤波器改变声音风格的说法不敢认同。dsd因为噪声整形把能量谱往高频区推,所以dsd在线性视图里看是往上翘曲的,滤波器只是功能组件,只有尽职和失职的区分。听感方面估计大伙都是一致的,dsd更柔和,但是相比pcm,dsd的动态明显被压缩了。dsd另一个明显的特点是信号幅度的增量不具有pcm那种极速跳变的可能。
作者: zasflower    时间: 2023-4-14 08:58
本帖最后由 zasflower 于 2023-4-14 09:01 编辑
leonbernieni 发表于 2023-4-14 08:57
滤波器改变声音风格的说法不敢认同。dsd因为噪声整形把能量谱往高频区推,所以dsd在线性视图里看是往上翘曲 ...

因为DSD那个电平变化是一个缓慢变化的过程,所以DSD才会电平低6db.

但就动态而言,DSD有大约24bit的动态,低频大约有32bit的动态。越靠近低频动态越高,越靠近高频动态越低。
作者: zasflower    时间: 2023-4-14 09:03
leonbernieni 发表于 2023-4-14 08:57
滤波器改变声音风格的说法不敢认同。dsd因为噪声整形把能量谱往高频区推,所以dsd在线性视图里看是往上翘曲 ...

滤波器确实会改变风格的。
你拿foobar2000和我那个对比。
https://github.com/sulinhuang/KMusicPlayerWindows/releases/tag/v171Release

播放同一首DSD,声音风格不同,不接解码器下我们都是DSD-PCM。算法和滤波器不同而已。

作者: leonbernieni    时间: 2023-4-14 09:05
zasflower 发表于 2023-4-14 08:58
因为DSD那个电平变化是一个缓慢变化的过程,所以DSD才会电平低6db.

6db是调制方式的差异,所以发行商有过半+6db的sacd碟发行。6db之外动态还是有压缩。
作者: jiangyb    时间: 2023-4-14 09:18
程序输入有错误,把0打成1了。所以不一样了。
作者: zasflower    时间: 2023-4-14 09:18
我还是坚持实践是检验真理的唯一标准。

DSD即使转换成PCM播放还是浓浓的DSD味道的。和CD同一首曲目一耳朵的差别。非常容易分辨。
作者: zasflower    时间: 2023-4-14 09:20
本帖最后由 zasflower 于 2023-4-14 09:25 编辑
jiangyb 发表于 2023-4-14 09:18
程序输入有错误,把0打成1了。所以不一样了。

这个是DSD文件规范,电脑0101是不会算错的。电脑的移位操作是不会错的。01错位必然破音。我之前把dsf文件的二进制0101反着读,就是破音。

声音出来正常,说明就代码写对了。代码有没有写正确,直接看运行结果就行了。


作者: alexiyan    时间: 2023-4-14 09:26
用DSD格式发行的音乐,还得用纯1bit解码才行啊,最大保真, 这也是金二,Cayin N7存在的理由
作者: abccbaa    时间: 2023-4-14 09:32
leonbernieni 发表于 2023-4-14 08:57
滤波器改变声音风格的说法不敢认同。dsd因为噪声整形把能量谱往高频区推,所以dsd在线性视图里看是往上翘曲 ...

我看了那个程序
这个程序其实可以改的
改了之后dsd也能出pcm 的大跳跃的声音
dsd怎么是反复滑动平均的算法
里面有几点不明白 为什么是2的13次方  为什么有92这个数字

作者: zasflower    时间: 2023-4-14 09:36
本帖最后由 zasflower 于 2023-4-14 11:27 编辑
abccbaa 发表于 2023-4-14 09:32
我看了那个程序
这个程序其实可以改的
改了之后dsd也能出pcm 的大跳跃的声音

92是DSF的文件规范,参见索尼的DSF文件白皮书。dsf文件的游标位置。dff文件就参见飞利浦的dff文件白皮书。
DSF规范是SONY的,DFF规范是飞利浦的。具体有点忘记了,因为这代码其实是6-7年前写的,现在只是做性能优化。

60则是dsf的56+4byte

2的13次方是8192,2 4 8 16 32 64 128 256 512这样。内存指数对齐。而且8192也是512的2的4次方倍数。512是dsf里面出现的。至于SONY为什么要规定512,我想是因为是2的指数。正如我什么要2的13次方,也是因为它是2的指数。毕竟0101方便对齐。

作者: abccbaa    时间: 2023-4-14 09:58
zasflower 发表于 2023-4-14 09:36
92是DSF的文件规范,参见索尼的DSF文件白皮书。dsf文件的游标位置。dff文件就参见飞利浦的dff文件白皮书 ...

它这反复跳1 2 3 4 5 6 7个bit 叠加 dsd格式是8bit代表一个时间点吗 最后一个是校验位?

dsd格式规范有没有 一直想看一下 就是dsd文件到底是什么形式的



作者: 番茄炒蛋饭    时间: 2023-4-14 10:00
dff和dsf的差异是文件头和DSD数据大小端差异 本质没啥差别
作者: 二师兄    时间: 2023-4-14 10:04
那为啥听感上,DSD的音色较CD的音色冷而且硬呢,我试过不少机器都是这样的音色
作者: zasflower    时间: 2023-4-14 10:04
本帖最后由 zasflower 于 2023-4-14 10:44 编辑
abccbaa 发表于 2023-4-14 09:32
我看了那个程序
这个程序其实可以改的
改了之后dsd也能出pcm 的大跳跃的声音

算法参见模拟电路的RC积分电路图。
https://baijiahao.baidu.com/s?id=1744091493295109131&wfr=spider&for=pc

这是积分器。



至于数学上的严格证明,有很多关于这个的严格数学推导。百度就很多了。

作者: zasflower    时间: 2023-4-14 10:05
二师兄 发表于 2023-4-14 10:04
那为啥听感上,DSD的音色较CD的音色冷而且硬呢,我试过不少机器都是这样的音色

那是机器的问题,DSD不硬,柔和。

作者: 番茄炒蛋饭    时间: 2023-4-14 10:08
zasflower 发表于 2023-4-14 10:04
算法参见模拟电路的RC积分电路图。
https://baijiahao.baidu.com/s?id=1744091493295109131&amp;wfr=spider&amp; ...

你的PCM播放也要过这个低通滤波器
作者: 二师兄    时间: 2023-4-14 10:12
zasflower 发表于 2023-4-14 10:05
那是机器的问题,DSD不硬,柔和。

我试过不少机器
马兰士红宝石CD 、力士D-03X CD、NAIM ATOM HE、SONY黑砖、双木三林DP5、乐图PAW6000等等
DSD的声音表现都是要比CD或者WAV硬,尤其是人声的表现,缺乏些感情

作者: zasflower    时间: 2023-4-14 10:12
番茄炒蛋饭 发表于 2023-4-14 10:00
dff和dsf的差异是文件头和DSD数据大小端差异 本质没啥差别

本质是数学那套,但是代码还是要区分dsf和dff的,不然高低位读错。

作者: zasflower    时间: 2023-4-14 10:13
本帖最后由 zasflower 于 2023-4-14 10:36 编辑
二师兄 发表于 2023-4-14 10:12
我试过不少机器
马兰士红宝石CD 、力士D-03X CD、NAIM ATOM HE、SONY黑砖、双木三林DP5、乐图PAW6000等 ...

那说明硬件有问题啊。。。

DSD更软更柔和的,相反,DSD的人声非常有感情。

DSD的特点,声音柔和,密度高。人声非常有感情,模拟味,声音细腻,声音安定。
作者: 番茄炒蛋饭    时间: 2023-4-14 10:14
噪音整形是DSD和PCM的差异的根本原因 噪音整形算法对噪音是高通滤波 对有效信号是低通滤波
作者: zasflower    时间: 2023-4-14 10:16
番茄炒蛋饭 发表于 2023-4-14 10:14
噪音整形是DSD和PCM的差异的根本原因 噪音整形算法对噪音是高通滤波 对有效信号是低通滤波

DSD本来就是把噪声往高频偏移啊。能量守恒。

高频噪声最终必然要移除的。

作者: 番茄炒蛋饭    时间: 2023-4-14 10:25
现在的DAC除了R2R架构 其他的都是DS架构 也就是说内部都会做噪音整形 除了CS厂其他几家都不是1bit ,大家听到的都是做过噪音整形,楼主说的差异就是多加了低通滤波带来的差异 以及多以一次噪音整形带来的差异
作者: zasflower    时间: 2023-4-14 10:27
本帖最后由 zasflower 于 2023-4-14 15:10 编辑

其实直接听最终声音就知道了,真理对不对,耳朵听结果就知道了。实践最容易检验真理是否正确。

DSD 和 CD 声音区别明显。

https://github.com/sulinhuang/KMusicPlayerWindows/releases/tag/v172Release
作者: leonbernieni    时间: 2023-4-14 11:01
番茄炒蛋饭 发表于 2023-4-14 10:25
现在的DAC除了R2R架构 其他的都是DS架构 也就是说内部都会做噪音整形 除了CS厂其他几家都不是1bit ,大家听 ...

+1
另外cd带限在22.05Hz也是个坑,现实世界不能全听香农的,把一个pcm脉冲信号转换为dsd后看频谱会塞满视图方框,按理dsd可以扩展通带,但是原信号又是cd通带,滤波后必然是衰减了能量谱。dxd的情况就好得多。其实从频域/时域分析思路会更清晰。
作者: 吉祥如意1973    时间: 2023-4-14 11:08
这个起点有点高啊?看不懂
作者: 番茄炒蛋饭    时间: 2023-4-14 11:11
leonbernieni 发表于 2023-4-14 11:01
+1
另外cd带限在22.05Hz也是个坑,现实世界不能全听香农的,把一个pcm脉冲信号转换为dsd后看频谱会塞满 ...

这个问题确实要频域/时域分析,噪音整形本质是为了提高精度更好的做数字转模拟 传统的R2R依赖电阻精度 成本极高,使用DS架构之后 对数字转模拟部分元件要求低多了,当然对数字电路要求很高,不过这个在现在不是问题。
作者: zasflower    时间: 2023-4-14 11:13
本帖最后由 zasflower 于 2023-4-14 11:18 编辑
abccbaa 发表于 2023-4-14 09:58
它这反复跳1 2 3 4 5 6 7个bit 叠加 dsd格式是8bit代表一个时间点吗 最后一个是校验位?

dsd格式规范 ...

8bit是cpu读取按照8bit 8bit来读取的,dsd是1bit的,所以我要反复移位。

叠加是加法器,参见rc模拟滤波器一阶电子电路里面的加法器。然后还有个乘法器。乘法器对应的是dsd模拟滤波器电子电路的电阻。

作者: clark8888    时间: 2023-4-14 12:14
同一个解码器dsd的动态测量指标都低于pcm
作者: abccbaa    时间: 2023-4-14 12:46
zasflower 发表于 2023-4-14 11:13
8bit是cpu读取按照8bit 8bit来读取的,dsd是1bit的,所以我要反复移位。

叠加是加法器,参见rc模拟滤 ...

ok  明白了 那r2r(1bit)的解码 是不是就不用先叠加成一个字节
但为什么r2r听上去比9038这种糊


作者: abccbaa    时间: 2023-4-14 12:49
番茄炒蛋饭 发表于 2023-4-14 10:00
dff和dsf的差异是文件头和DSD数据大小端差异 本质没啥差别

有没有格式文件看看

作者: 番茄炒蛋饭    时间: 2023-4-14 13:23
abccbaa 发表于 2023-4-14 12:49
有没有格式文件看看

网上有格式说明文档 你找找应该能找到 格式相当简单比flac ape这种格式简单太多
作者: abccbaa    时间: 2023-4-14 13:28
本帖最后由 abccbaa 于 2023-4-14 13:29 编辑
番茄炒蛋饭 发表于 2023-4-14 13:23
网上有格式说明文档 你找找应该能找到 格式相当简单比flac ape这种格式简单太多

相对dsd  我觉得dxd更好听 dsd动态有损失

作者: 番茄炒蛋饭    时间: 2023-4-14 13:33
abccbaa 发表于 2023-4-14 13:28
相对dsd  我觉得dxd更好听 dsd动态有损失

我最近开发了PCM转DSD播放器 用DAC直解DSD 音质动态都不错 本身DAC内部也是PCM转DSD模块 但因为元件限制和精度问题 算法有取舍 在PC上使用更高阶算法然后直连DAC 会比PCM音质好
作者: leonbernieni    时间: 2023-4-14 13:45
番茄炒蛋饭 发表于 2023-4-14 13:33
我最近开发了PCM转DSD播放器 用DAC直解DSD 音质动态都不错 本身DAC内部也是PCM转DSD模块 但因为元件限制 ...

能自己搭转换器是一种享受!我对代码是一窍不通有没耐心学。据说用纯加法器比直接调用乘法模块更好,直觉上。
作者: 番茄炒蛋饭    时间: 2023-4-14 13:45
abccbaa 发表于 2023-4-14 12:46
ok  明白了 那r2r(1bit)的解码 是不是就不用先叠加成一个字节
但为什么r2r听上去比9038这种糊

R2R糊是因为电阻矩阵精度不高 细节丢失严重,DSD解码对元件精度要求不高 可以做到很高精度 所以现在主流DAC先转类DSD格式再赚模拟信号
作者: 番茄炒蛋饭    时间: 2023-4-14 13:49
leonbernieni 发表于 2023-4-14 13:45
能自己搭转换器是一种享受!我对代码是一窍不通有没耐心学。据说用纯加法器比直接调用乘法模块更好,直觉 ...

大部分滤波器都需要乘法,重采样和噪音整形都有大量乘法 这个避免不了
作者: leonbernieni    时间: 2023-4-14 15:07
番茄炒蛋饭 发表于 2023-4-14 13:49
大部分滤波器都需要乘法,重采样和噪音整形都有大量乘法 这个避免不了

可以实现,你搜一个Simple DSD modulator for DSC2的帖子,提到个CSD的文档,有个性的“大作业”!
作者: zasflower    时间: 2023-4-14 15:29
二师兄 发表于 2023-4-14 10:12
我试过不少机器
马兰士红宝石CD 、力士D-03X CD、NAIM ATOM HE、SONY黑砖、双木三林DP5、乐图PAW6000等 ...

我写了个小软件。我自己听歌就用自己写的软件听,可以播放CD DSD。
https://github.com/sulinhuang/KMusicPlayerWindows/releases/tag/v172Release




你先按照图片这样设置,然后再打开一首DSD64,看声音是不是不硬很柔和,和CD区别明显。

这个是DSD这个格式的特点。


作者: 番茄炒蛋饭    时间: 2023-4-14 15:31
leonbernieni 发表于 2023-4-14 15:07
可以实现,你搜一个Simple DSD modulator for DSC2的帖子,提到个CSD的文档,有个性的“大作业”!

早就看过 他需要fpga来做插值和噪音整形 他的fpga算法我都写过,很熟
作者: 冰蓝幻狐    时间: 2023-4-14 17:38
仰望
作者: hytcqq    时间: 2023-4-16 01:06
本帖最后由 hytcqq 于 2023-4-16 01:07 编辑

既然用cpu了,就不要用一阶二阶这些初级的滤波器了,而且你没有加噪声整形,dsp工具箱里很多高级滤波器,tap动辄256的,这也是hqplayer厉害的地方。
作者: 欢欢乐乐    时间: 2023-4-16 07:47
番茄炒蛋饭 发表于 2023-4-14 13:33
我最近开发了PCM转DSD播放器 用DAC直解DSD 音质动态都不错 本身DAC内部也是PCM转DSD模块 但因为元件限制 ...

非常正确,&#128077;&#127995;&#128077;&#127995;

作者: 欢欢乐乐    时间: 2023-4-16 07:49
番茄炒蛋饭 发表于 2023-4-14 13:33
我最近开发了PCM转DSD播放器 用DAC直解DSD 音质动态都不错 本身DAC内部也是PCM转DSD模块 但因为元件限制 ...

非常正确,顶





欢迎光临 耳机网-耳机大家坛 (http://erji.net/) Powered by Discuz! X3.2