我最近看到了这个 YouTube 视频,它显示了一个 PCM 流正在通过 1 位内部“PC 扬声器”播放,经过数小时的谷歌搜索,我开始认为这是通过 Sigma-Delta 调制完成的。
到目前为止,我从该过程中收集到的是,它采用原始数字信号,通过 PWM 对其进行过采样和近似,同时对 1 位输出应用低通滤波器。我不太确定这是否正确。
没有 DSP 的背景,我想知道:
这个过程如何在软件中实现?
我最近看到了这个 YouTube 视频,它显示了一个 PCM 流正在通过 1 位内部“PC 扬声器”播放,经过数小时的谷歌搜索,我开始认为这是通过 Sigma-Delta 调制完成的。
到目前为止,我从该过程中收集到的是,它采用原始数字信号,通过 PWM 对其进行过采样和近似,同时对 1 位输出应用低通滤波器。我不太确定这是否正确。
没有 DSP 的背景,我想知道:
这个过程如何在软件中实现?
将信号上采样到所需频率。您可以使用线性插值(质量差但简单快速)或正弦插值(质量好但速度慢)。例如,致力于高质量重采样的库,请参见 libsoxr:http: //sourceforge.net/p/soxr/wiki/Home/
使用噪声整形执行抖动。您需要对抖动信号进行滤波,以最大限度地减少 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;