加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – 正确实施杜比定向逻辑II

发布时间:2020-12-15 04:23:24 所属栏目:百科 来源:网络整理
导读:到目前为止,我已经成功实现了基于以下 specifications的杜比矩阵解码器: 左右 中心0.707 0.707 离开1 0 正确0 1 SLeft 0.871 0.489 SRight 0.489 0.871 但是在测试了我的矩阵之后,我发现它有很多削波,它听起来不像杜比的解码器.我对DSP比较陌生,虽然我很确
到目前为止,我已经成功实现了基于以下 specifications的杜比矩阵解码器:

左右
中心0.707 0.707
离开1 0
正确0 1
SLeft 0.871 0.489
SRight 0.489 0.871

但是在测试了我的矩阵之后,我发现它有很多削波,它听起来不像杜比的解码器.我对DSP比较陌生,虽然我很确定我理解大多数基础知识;但是我仍然对于导致这种情况发生的原因一无所知,我是否遗漏了规范中的内容,还是仅仅是我的代码?

我目前的矩阵解码器,

private static void DolbyProLogicII(List<float> leftSamples,List<float> rightSamples,int sampleRate,string outputDirectory)
{
    // WavFileWrite is a wrapper class for NAudio to create Wav files. 
    var meta = new WaveFormat(sampleRate,16,1);
    var c = new WavFileWrite { MetaData = meta,FileName = Path.Combine(outputDirectory,"c.wav") };
    var l = new WavFileWrite { MetaData = meta,"l.wav") };
    var r = new WavFileWrite { MetaData = meta,"r.wav") };
    var sl = new WavFileWrite { MetaData = meta,"sl.wav") };
    var sr = new WavFileWrite { MetaData = meta,"sr.wav") };

    var ii = (leftSamples.Count > rightSamples.Count ? rightSamples.Count : leftSamples.Count);

    // Process center channel.
    for (var i = 0; i < ii; i++)
    {
        c.MonoChannelAudioData.Add((leftSamples[i] * 0.707) + (rightSamples[i] * 0.707));
    }
    c.Flush();


    // Process left channel.
    l.MonoChannelAudioData = leftSamples;
    l.Flush();


    // Process right channel.
    r.MonoChannelAudioData = rightSamples;
    r.Flush();


    // Process surround left channel.
    for (var i = 0; i < ii - 1; i++)
    {
        sl.MonoChannelAudioData.Add((leftSamples[i] * 0.871) + (rightSamples[i] * 0.489));
    }
    sl.Flush();


    // Process surround right channel.
    for (var i = 0; i < ii - 1; i++)
    {
        sr.MonoChannelAudioData.Add((leftSamples[i] * 0.489) + (rightSamples[i] * 0.871));
    }
    sr.Flush();
}

解决方法

文艺青年最爱的

要严格地将您的实施与杜比的规格进行比较,您会遗漏几件事,

>解码器的混音级别(不是比率)不正确,
> LFE channel未处理,
>左环绕&环绕右声道不是phase shifted,delayed,也不是通过HPF,
>中心频道不通过Bandpass.

正确的组合

削波问题是你的maxtrix混合水平(比率很好)的结果,例如,自0.707 0.707 = 1.414以来,中心通道超过了41.4%;因此,要保持正确的比例,只需将混合水平减半即可.

考虑到这一点,你的maxtrix现在应该是这样的,

左右
中心0.354 0.354
左0.5 0
正确0 0.5
SLeft 0.436 0.245
SRight 0.245 0.436

添加LFE

如果您没有故意遗漏LFE频道,您可以像中央频道一样对其进行解码,但您必须以120Hz(杜比标准)应用LPF.

使环绕声道实际上“环绕”

要处理左环绕和右环绕通道,您需要通过HPF以100 Hz的频率通过左右声道,然后应用90°相移(然后反转左侧);混合,然后添加一个延迟(我相信杜比没有指定确切的时间量,但经过一些测试后,我发现“甜点”似乎是大约10毫秒).

(如果你打算延迟使用,我建议在5ms到12.5ms之间这样做(随意试验).如果延迟太小,你最终会得到“压缩/浓缩”的声音混合,太长时间,你最终会在后台发出可怕的高频回声.理想情况下,最终结果听起来应该是“通风/开放”,但没有任何回音/混响的暗示.)

leftSamples  -> HPF -> phase shift -> invert -> mix 
                                                     -> delay
rightSamples -> HPF ->      phase shift      -> mix /

中心频道

杜比还指定中心声道需要通过70Hz至20kHz的带通(混合后).

最后…

所以,把所有这些放在一起你应该得到以下结果,

public void DolbyProLogicII(float[] leftSamples,float[] rightSamples,int sampleRate)
{
    var ii = Math.Min(leftSamples.Length,rightSamples.Length);
    var c = new float[ii];
    var l = new float[ii];
    var r = new float[ii];
    var sl = new float[ii];
    var sr = new float[ii];
    var lfe = new float[ii];



    // Process center channel
    for (var i = 0; i < ii; i++)
    {
        // Best to be as precise as possible.
        c[i] = (leftSamples[i] * 0.35355339059327376220042218105242f) + (rightSamples[i] * 0.35355339059327376220042218105242f);
    }
    c = LinkwitzRileyHighPass(c,sampleRate,70);
    c = LinkwitzRileyLowPass(c,20000);



    // Process front left channel
    for (var i = 0; i < ii; i++)
    {
        l[i] = leftSamples[i] * 0.5f;
    }



    // Process front right channel
    for (var i = 0; i < ii; i++)
    {
        r[i] = rightSamples[i] * 0.5f;
    }



    // Process left samples for SL channel
    var slL = new float[ii];
    for (var i = 0; i < ii; i++)
    {
        slL[ii] = leftSamples[i] * 0.43588989435406735522369819838596f;
    }
    slL = LinkwitzRileyHighPass(slL,100);
    slL = PhaseShift(slL,true);

    // Process right samples for SL channel.
    var slR = new float[ii];
    for (var i = 0; i < ii; i++)
    {
        slR[i] = rightSamples[i] * 0.24494897427831780981972840747059f;
    }
    slR = LinkwitzRileyHighPass(slR,100);
    slR = PhaseShift(slR,sampleRate);

    // Combine new left & right samples for SL channel
    for (var i = 0; i < ii - 1; i++)
    {
        sl[i] = slL[i] + slR[i];
    }
    sl = Delay(sl,10);



    // Process left samples for SR channel
    var srL = new float[ii];
    for (var i = 0; i < ii; i++)
    {
        srL[i] = leftSamples[i] * 0.24494897427831780981972840747059f;
    }
    srL = LinkwitzRileyHighPass(srL,100);
    srL = PhaseShift(srL,true);

    // Process right samples for SR channel
    var srR = new float[ii];
    for (var i = 0; i < ii; i++)
    {
        srR[i] = rightSamples[i] * 0.43588989435406735522369819838596f;
    }
    srR = LinkwitzRileyHighPass(srR,100);
    srR = PhaseShift(srR,sampleRate);

    // Combine new left & right samples for SR channel
    for (var i = 0; i < ii - 1; i++)
    {
        sr[i] = srL[i] + srR[i];
    }
    sr = Delay(sr,10);



    // Process LFE channel.
    for (var i = 0; i < ii; i++)
    {
        lfe[i] = (leftSamples[i] * 0.35355339059327376220042218105242f) + (rightSamples[i] * 0.35355339059327376220042218105242f);
    }
    lfe = LinkwitzRileyLowPass(lfe,120);
}

public float[] PhaseShift(float[] samples,double sampleRate,bool invertOutput = false)
{
    var depth = 4.0;
    var delay = 100.0;
    var rate = 0.1;
    var newSamples = new float[samples.Length];

    double wp,min_wp,max_wp,range,coef,sweepfac;
    double inval,x1,outval = 0.0;
    double lx1 = 0.0,ly1 = 0.0,lx2 = 0.0,ly2 = 0.0,lx3 = 0.0,ly3 = 0.0,lx4 = 0.0,ly4 = 0.0;

    // calc params for sweeping filters
    wp = min_wp = (Math.PI * delay) / sampleRate;
    range = Math.Pow(2.0,depth);
    max_wp = (Math.PI * delay * range) / sampleRate;
    rate = Math.Pow(range,rate / (sampleRate / 2));
    sweepfac = rate;

    for (var i = 0; i < samples.Length; i++)
    {
        coef = (1.0 - wp) / (1.0 + wp);     // calc coef for current freq

        x1 = (inval = (double)samples[i]);
        ly1 = coef * (ly1 + x1) - lx1;      // do 1st filter
        lx1 = x1;
        ly2 = coef * (ly2 + ly1) - lx2;     // do 2nd filter 
        lx2 = ly1;
        ly3 = coef * (ly3 + ly2) - lx3;     // do 3rd filter 
        lx3 = ly2;
        ly4 = coef * (ly4 + ly3) - lx4;     // do 4th filter 
        lx4 = ly3;

        // final output
        outval = ly4;

        if (invertOutput)
        {
            newSamples[i] = -(float)outval;
        }
        else
        {
            newSamples[i] = (float)outval;
        }

        wp *= sweepfac;            // adjust freq of filters 

        if (wp > max_wp)           // max?
        {
            sweepfac = 1.0 / rate; // sweep back down
        }
        else
        {
            if (wp < min_wp)       // min?
            {
                sweepfac = rate;   // sweep back up
            }
        }
    }

    return newSamples;
}

public float[] Delay(float[] samples,double milliseconds)
{
    var output = new List<float>(samples);
    var ii = (sampleRate / 1000) * milliseconds;

    for (var i = 0; i < ii; i++)
    {
        output.Insert(0,0);
    }

    return output.ToArray();
}

public float[] LinkwitzRileyHighPass(float[] samples,double cutoff)
{
    if (cutoff <= 0 && cutoff >= sampleRate / 2)
    {
        throw new ArgumentOutOfRangeException("cutoff","The cutoff frequency must be between 0 and "sampleRate" / 2.");
    }

    if (sampleRate <= 0)
    {
        throw new ArgumentOutOfRangeException("sampleRate","The sample rate must be more than 0.");
    }

    if (samples == null || samples.Length == 0)
    {
        throw new ArgumentNullException("samples",""samples" can not be null or empty.");
    }

    var newSamples = new float[samples.Length];
    var wc = 2 * Math.PI * cutoff;
    var wc2 = wc * wc;
    var wc3 = wc2 * wc;
    var wc4 = wc2 * wc2;
    var k = wc / Math.Tan(Math.PI * cutoff / sampleRate);
    var k2 = k * k;
    var k3 = k2 * k;
    var k4 = k2 * k2;
    var sqrt2 = Math.Sqrt(2);
    var sq_tmp1 = sqrt2 * wc3 * k;
    var sq_tmp2 = sqrt2 * wc * k3;
    var a_tmp = 4 * wc2 * k2 + 2 * sq_tmp1 + k4 + 2 * sq_tmp2 + wc4;

    var b1 = (4 * (wc4 + sq_tmp1 - k4 - sq_tmp2)) / a_tmp;
    var b2 = (6 * wc4 - 8 * wc2 * k2 + 6 * k4) / a_tmp;
    var b3 = (4 * (wc4 - sq_tmp1 + sq_tmp2 - k4)) / a_tmp;
    var b4 = (k4 - 2 * sq_tmp1 + wc4 - 2 * sq_tmp2 + 4 * wc2 * k2) / a_tmp;

    var a0 = k4 / a_tmp;
    var a1 = -4 * k4 / a_tmp;
    var a2 = 6 * k4 / a_tmp;
    var a3 = a1;
    var a4 = a0;

    double ym1 = 0.0,ym2 = 0.0,ym3 = 0.0,ym4 = 0.0,xm1 = 0.0,xm2 = 0.0,xm3 = 0.0,xm4 = 0.0,tempy = 0.0;

    for (var i = 0; i < samples.Length; i++)
    {
        var tempx = samples[i];

        tempy = a0 * tempx + a1 * xm1 + a2 * xm2 + a3 * xm3 + a4 * xm4 - b1 * ym1 - b2 * ym2 - b3 * ym3 - b4 * ym4;
        xm4 = xm3;
        xm3 = xm2;
        xm2 = xm1;
        xm1 = tempx;
        ym4 = ym3;
        ym3 = ym2;
        ym2 = ym1;
        ym1 = tempy;

        newSamples[i] = (float)tempy;
    }

    return newSamples;
}

public float[] LinkwitzRileyLowPass(float[] samples,""samples" can not be null or empty.");
    }

    var newSamples = new float[samples.Length];
    var wc = 2 * Math.PI * cutoff;
    var wc2 = wc * wc;
    var wc3 = wc2 * wc;
    var wc4 = wc2 * wc2;
    var k = wc / Math.Tan(Math.PI * cutoff / sampleRate);
    var k2 = k * k;
    var k3 = k2 * k;
    var k4 = k2 * k2;
    var sqrt2 = Math.Sqrt(2);
    var sq_tmp1 = sqrt2 * wc3 * k;
    var sq_tmp2 = sqrt2 * wc * k3;
    var a_tmp = 4 * wc2 * k2 + 2 * sq_tmp1 + k4 + 2 * sq_tmp2 + wc4;

    var b1 = (4 * (wc4 + sq_tmp1 - k4 - sq_tmp2)) / a_tmp;
    var b2 = (6 * wc4 - 8 * wc2 * k2 + 6 * k4) / a_tmp;
    var b3 = (4 * (wc4 - sq_tmp1 + sq_tmp2 - k4)) / a_tmp;
    var b4 = (k4 - 2 * sq_tmp1 + wc4 - 2 * sq_tmp2 + 4 * wc2 * k2) / a_tmp;

    var a0 = wc4 / a_tmp;
    var a1 = 4 * wc4 / a_tmp;
    var a2 = 6 * wc4 / a_tmp;
    var a3 = a1;
    var a4 = a0;

    double ym1 = 0.0,tempy = 0.0;

    for (var i = 0; i < samples.Length; i++)
    {
        var tempx = samples[i];

        tempy = a0 * tempx + a1 * xm1 + a2 * xm2 + a3 * xm3 + a4 * xm4 - b1 * ym1 - b2 * ym2 - b3 * ym3 - b4 * ym4;
        xm4 = xm3;
        xm3 = xm2;
        xm2 = xm1;
        xm1 = tempx;
        ym4 = ym3;
        ym3 = ym2;
        ym2 = ym1;
        ym1 = tempy;

        newSamples[i] = (float)tempy;
    }

    return newSamples;
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读