sigma-delta 调制如何在软件中工作?

信息处理 声音的 调制 软件实现 delta-sigma
2022-02-09 17:03:14

我最近看到了这个 YouTube 视频,它显示了一个 PCM 流正在通过 1 位内部“PC 扬声器”播放,经过数小时的谷歌搜索,我开始认为这是通过 Sigma-Delta 调制完成的。

到目前为止,我从该过程中收集到的是,它采用原始数字信号,通过 PWM 对其进行过采样和近似,同时对 1 位输出应用低通滤波器。我不太确定这是否正确。

没有 DSP 的背景,我想知道:
这个过程如何在软件中实现?

3个回答
  1. 将信号上采样到所需频率。您可以使用线性插值(质量差但简单快速)或正弦插值(质量好但速度慢)。例如,致力于高质量重采样的库,请参见 libsoxr:http: //sourceforge.net/p/soxr/wiki/Home/

  2. 使用噪声整形执行抖动。您需要对抖动信号进行滤波,以最大限度地减少 0-20000 Hz 范围内的噪声,同时在超声波范围内增加它。有关噪声整形的详细信息,请参阅此出色的文档:http ://www.beis.de/Elektronik/DeltaSigma/DeltaSigma.html ,尤其是图 9,它显示了在反馈回路中添加噪声。

这就是 delta-sigma DAC 的工作原理,这当然可以在软件中实现。

PWM 需要对 16 位音频进行 65536× 上采样,因此不使用它。取而代之的是 PDM(脉冲密度调制),它与 delta-sigma 调制基本相同。而且它只需要对 Hi-Fi 音频进行 64 倍上采样。

这是一个详细的实现,可以提供进一步的见解。这是我用 Python 实现的二阶 Sigma Delta DAC。简化但功能齐全的代码如下(为清楚起见,省略了有关位和周期精确操作的详细信息)。

下面是功能框图。由于反馈结构,专门提供了噪声整形。(有关详细信息,请参阅我的答案:如何绘制一阶增量 Sigma Delta ADC 输出的噪声形频谱?)。这与跟踪参考(低通滤波器到参考输入,这是所需电平)但允许受控设备的高频通过(高通滤波器到量化噪声)的任何控制回路没有什么不同。

框图

示例 Python 代码

def dsigma(input_values, width):
    # create output by iterating through input_values
    for next_sample in input_values:

        # compute next state (clock update)
        sum1d = sum1
        sum2d = sum2

        # asynchronous operations
        out = -1 if sum2d < 0 else 1    
        fb = -2**(width-1) if sum2d < 0 else 2**(width-1)-1
        fbx2 = 2*fb
        delta1 = next_sample - fb
        delta2 = sum1d - fbx2

        sum1 = sum1d + delta1
        sum2 = sum2d + delta2
        yield out

这是一个 python 生成器,因此您可以使用列表或循环来生成每个值。例如,下面显示了 24 位输入的 5 个样本的简单情况。(您需要生成更长的样本集才能看到 Sigma Delta PWM 操作的实际效果):

out = dsigma([25,25,25,25,25], 24)
print(list(out))

这个简单示例的输出频谱演示了噪声整形,它在过滤后从两级输出中获得更高的有效精度(消除所有高频噪声并提供表示 2 级 PWM 信号平均值的精确输出电平。)

光谱

干草我找到了我的 sigma delta MATLAB 程序。不知道好不好。

%
%
%
%
%
%
%   simulated 1 bit sigma-delta converter:
%
%
%            x(n)-y(n-1)    w(n)                v(n)                 ( mean(y^2) = A^2 )
%
%   x ---->(+)--->[1/(z-1)]--->(+)--->[1/(z-1)]--->[Quantizer]----.---> y = +/- A = quantized value
%           ^                   ^                                 |
%           |                   |                                 |
%           |                   '----[-fbg]<----.                 |
%           |                                   |                 |
%           '------[-1]<------------------------'------[1/z]<-----'
%
%
%
%
%
%   "linearized" model:
%                                                          .---- q = quantization noise  ( mean(q) = 0 )
%                                                          |
%                                                          |
%            x - y/z        w                   v          |         ( mean(y^2) = G^2*mean(v^2) + mean(q^2) )
%                                                          v
%   x ---->(+)--->[1/(z-1)]--->(+)--->[1/(z-1)]--->[G]--->(+)-----.---> y = G*v + q
%           ^                   ^                                 |
%           |                   |                                 |
%           |                   '----[-fbg]<----.                 |
%           |                                   |                 |
%           '------[-1]<------------------------'------[1/z]<-----'
%
%
%
%
%
%
%
%
%           W = 1/(z-1)*(X - Y/z)
%
%
%           V = 1/(z-1)*(W - fbg*Y/z) 
%
%             = (X - Y/z - fbg*Y*(z-1)/z)/(z-1)^2
%
%             = (X*z - Y*(1+fbg*(z-1))) / (z*(z-1)^2)
%
%
%           Y = G*V + Q = G*(X*z - Y*(1+fbg*(z-1)))/(z*(z-1)^2) + Q
%
%             = G*X/(z-1)^2 - G*Y*(1+fbg*(z-1))/(z*(z-1)^2) + Q
%
%
%           Y + G*Y*(1-fbg + fbg*z)/(z*(z-1)^2) = G*X/(z-1)^2 + Q
%
%
%           Y = (G*X/(z-1)^2 + Q)/(1 + G*(1-fbg + fbg*z)/(z*(z-1)^2))
%
%             = (G*X/(z-1)^2 + Q)*(z*(z-1)^2)/((z*(z-1)^2) + G*(1-fbg + fbg*z))
%
%             = z*(G*X + Q*(z-1)^2)/(z^3 - 2*z^2 + (G*fbg+1)*z + G*(1-fbg))
%
%             = z*(G*X + Q*(z-1)^2)/(z*(z-1)^2 + G*fbg*z + G*(1-fbga))
%
%
%    as z -> 1  (DC)
%
%           Y  ->  z*X/(fbg*z + (1-fbg)) =  X/(fbg + (1-fbg)/z)  -->  X
%
%



if ~exist('mean_vv', 'var')
    linearized_model = 0                % run this with 0 the first time to define G and mean(q^2)
end

if ~exist('A', 'var')
    A = 1.0                             % comparator output magnitude
end

if ~exist('fbg', 'var')
    fbg = 2.0                           % feedback gain to internal integrator
end

%
%   if there is an input soundfile specified, use it.  else, create a sin wave
%


if exist('inputFile', 'var')

    [inputBuffer, Fs] = audioread(inputFile);

    fileSize = length(inputBuffer);

    numSamples = 2.^(ceil(log2(fileSize(1))));  % round up to nearest power of 2

    x = zeros(numSamples, 1);                   % zero pad if necessary

    x(1:fileSize) = inputBuffer(:,1);           % if multi-channel, use left channel only

    clear inputBuffer;                          % free this memory
    clear fileSize;

    t = linspace(0.0, (numSamples-1)/Fs, numSamples);   % time

else

    if ~exist('numSamples', 'var')
        numSamples = 65536                              % number of samples in simulation
    end

    if ~exist('Fs', 'var')
        Fs = 44100                                      % (oversampled) sample rate
    end

    if ~exist('f0', 'var')
        f0 = 261.6255653                                % input freq (middle C)
    end

    if ~exist('Amplitude', 'var')
        Amplitude = 0.25                                % input amplitude
    end

    t = linspace(0.0, (numSamples-1)/Fs, numSamples);   % time
    x = Amplitude*cos(2*pi*f0*t);                       % the input

end

sound(x, Fs);                                   % listen to input sound
pause;

y = zeros(1, numSamples);                       % the output (created and initialized for speed later) 

if linearized_model
                                                % artificial quantization noise for linearized model
                                                % mean(q) = 0, var(q) = mean(q^2) = mean(y^2) - G^2*mean(v^2)
                                                % does not have to be uniform or triangle p.d.f.
    q = sqrt(6.0*(A^2 - G^2*mean_vv))*( rand(1, numSamples) - rand(1, numSamples) );
else
    q = zeros(1, numSamples);
end

sum_yv = 0.0;
sum_vv = 0.0;

w = 0;
v = 0;
for n = 1:numSamples

    if linearized_model

        y(n) = G*v + q(n);                      % here the comparator is modelled as a little gain with additive noise

    else

        if (v >= 0)                             % the comparator
            y(n) = +A;
        else
            y(n) = -A;
        end

        q(n) = y(n) - (sum_vv+1e-20)/(sum_yv+1e-20)*v;

    end

    sum_yv = sum_yv + y(n)*v;                   % collect some statistics on v
    sum_vv = sum_vv +    v*v;

    v = v + w  - fbg*y(n);                      % second integrator
    w = w + x(n) - y(n);                        % first integrator

end

if ~linearized_model                            % don't recalculate this if using the linearized model
    mean_yv = sum_yv/numSamples;
    mean_vv = sum_vv/numSamples;
    G = mean_yv/mean_vv;                        % the apparent comparator gain (assuming stationary input)
end

%
%
%
%     Y = ((G*z)*X + (z^3 - 2*z^2 + z)*Q) / (z^3 - 2*z^2 + (G*a+1)*z + G*(1-a))
%
%
%
Hx = freqz([0  0 G 0], [1 -2 G*fbg+1 G*(1-fbg)], numSamples/2);
Hq = freqz([1 -2 1 0], [1 -2 G*fbg+1 G*(1-fbg)], numSamples/2);



plot(t, y, 'b');
sound(y, Fs);                                   % this could sound pretty bad
pause;


Y = fft(fftshift(y .* kaiser(numSamples, 5.0)'));
Q = fft(fftshift(q .* kaiser(numSamples, 5.0)'));

f = linspace(0.0, (numSamples/2-1)/numSamples*Fs, numSamples/2);

plot(f, 20*log10(abs(Y(1:numSamples/2)) + 1e-10), 'b');
hold on;
plot(f, 20*log10(abs(Q(1:numSamples/2)) + 1e-10), 'r');
plot(f, 20*log10(abs(Hq) + 1e-10), 'g');
axis([0 Fs/2 -50 100]);
hold off;
pause;

semilogx(f(2:numSamples/2), 20*log10(abs(Y(2:numSamples/2)) + 1e-10), 'b');
hold on;
semilogx(f(2:numSamples/2), 20*log10(abs(Q(2:numSamples/2)) + 1e-10), 'r');
semilogx(f(2:numSamples/2), 20*log10(abs(Hq(2:numSamples/2)) + 1e-10), 'g');
axis([Fs/numSamples Fs/2 -50 100]);
hold off;
pause;


semilogx(f(2:numSamples/2), 20*log10(abs(Y(2:numSamples/2)) + 1e-10), 'b');
hold on;
semilogx(f(2:numSamples/2), 20*log10(abs(Hq(2:numSamples/2)) + 1e-10), 'r');
semilogx(f(2:numSamples/2), 20*log10(abs(Hx(2:numSamples/2)) + 1e-10), 'g');
axis([Fs/numSamples Fs/2 -50 110]);
hold off;