-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdsdemo4_audio.jl
327 lines (271 loc) Β· 9.46 KB
/
dsdemo4_audio.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
#dsdemo4_audio.jl: A GUI-based demonstration of delta-sigma with sound (Currently GUI-less).
using RSDeltaSigmaPort
@warn("dsdemo4_audio requires additional libraries:\n - MAT.jl\n - WAV.jl")
if !@isdefined(DSAudioDemo) #Hack to avoid continuously appending docs
@doc """# `DSAudioDemo`: Delta-Sigma Audio Demo Module
- Set source, modulator and decimation filter paramters,
- then click "Go."
- Click on input or output waveforms to listen to them.
- Click on the output spectrum to listen to the amplified error.
## TODO
- Implement GUI, as originally intended.
""" DSAudioDemo
end
module DSAudioDemo
using RSDeltaSigmaPort
using RSDeltaSigmaPort.EasyPlot #set, cons
using RSDeltaSigmaPort: fft
using RSDeltaSigmaPort: linlin, loglin
import Printf: @sprintf
import MAT, WAV
#==Constants
===============================================================================#
const πβout = 8192 #Output sample rate is fixed
const j=im
"ABCD matrices of pre-defined modulators:"
const MODABCD_PRESETS = Dict{Int, Array}(
1 => [1 1 -1; 1 0 0],
2 => [1 0 1 -1; 1 1 1 -2; 0 1 0 0],
)
#==Types
===============================================================================#
abstract type AbstractSource; end
struct SrcRamp <: AbstractSource; end
mutable struct SrcRaw <: AbstractSource
u::Vector #{Float64} #Oversampled signal
u0::Vector #{Float64} #Decimated signal
end
mutable struct SrcSine <: AbstractSource
f::Int #Frequency (Hz)
A::Float64
end
SrcSine(;f=500, A=0.5) = SrcSine(Int(f), A)
struct Modulator
sys
fsout::Int #Output sample rate
OSR::Int
sincorder::Int
end
#==Constructors
===============================================================================#
function _Modulator(sys, πββ::Int, OSR::Nothing, sincorder::Int)
#Round πββ to a multiple of πβout:
OSR = round(Int, πββ/πβout)
OSR = max(1, OSR)
πββnew = πβout*OSR #πβout is always hardcoded in dsdemo4
if πββ < πβout
@warn("Requested (over)-sampling frequency, πββ < πβout." *
"\nSetting πββ = πβout = $πβout Hz."
)
elseif πββ != πββnew
@warn("Adjusting (over)-sampling frequency\nfrom πββ = $πββ" *
"\nto πββ = $πββnew (OSR = $OSR)."
)
end
return Modulator(sys, πβout, OSR, sincorder)
end
_Modulator(sys, πββ::Nothing, OSR::Int, sincorder::Int) =
Modulator(sys, πβout, OSR, sincorder) #πβout is always hardcoded in dsdemo4
_Modulator(sys, πββ::Nothing, OSR::Nothing, sincorder::Int) =
_Modulator(sys, πββ, 32, sincorder) #Default OSR
_Modulator(sys, πββ, OSR, sincorder::Int) =
throw(ArgumentError("Modulator: Invalid type for πββ or OSR (or overspecified)."))
Modulator(preset; fos=nothing, OSR=nothing, sincorder::Int=2) =
_Modulator(MODABCD_PRESETS[preset], fos, OSR, sincorder)
#==Helper functions/ data generators
===============================================================================#
function validate(m::Modulator)
(m.OSR < 1) &&
throw("Modulator: Invalid OSR value: $(m.OSR).")
return
end
function generate(src::SrcSine, m::Modulator; N=10)
(src.f > m.fsout/2) &&
throw("Source π = $(src.f) too high. Must be <= πβout/2.")
πββ = m.fsout*m.OSR
πβ = 2Ο*src.f
tidx = (0:N-1); t = tidx/πββ
u = src.A * sin.((πβ/πββ) * tidx) .* ds_hann(N)
u0 = u[1:m.OSR:end]
return (t, u, u0)
end
function generate(src::SrcRamp, m::Modulator; N=10)
πββ = m.fsout*m.OSR
tidx = (0:N-1); t = tidx/πββ
u = collect(range(-0.7, stop=0.7, length=N))
u0 = u[1:m.OSR:end]
return (t, u, u0)
end
function generate(src::SrcRaw, m::Modulator; N=10)
πββ = m.fsout*m.OSR
N = length(src.u)
tidx = (0:N-1); t = tidx/πββ
return (t, src.u, src.u0)
end
function play(data::Vector)
WAV.wavplay(data, πβout)
return :PLAYBACK_COMLETE
end
#==Main algorithms
===============================================================================#
function run(m::Modulator; Tsim::Float64=2.0, input=SrcSine(f=500))
validate(m)
(m.fsout != πβout) && throw("dsdemo4 ony supports modulation schemes where πβout = $πβout.")
fos = πβout*m.OSR #(over)-sampling frequency
#Compute signal `u` depending on desired source:
N = round(Int, Tsim*fos) #Unless overwritten
(t, u, u0) = generate(input, m, N=round(Int, Tsim*fos))
OSR = m.OSR #Copy for string interpolation
#Plot signal `u0`
plot_tdec = cons(:plot, linlin, title="Decimated Signal (dec=$OSR)", legend=false,
xyaxes=set(ymin=-1, ymax=1),
labels=set(xaxis="Time [s]", yaxis="uβ(t), w(t)"),
)
N = length(u0)
t = (0:N-1)/πβout
u0w = waveform(t, u0)
push!(plot_tdec,
cons(:wfrm, u0w, line=set(style=:solid, color=:blue, width=2), label="uβ"),
)
#Plot U(π), from 0 to πβout/2
plot_πdec = cons(:plot, linlin, title="Spectrum (dec=$OSR)", legend=false,
xyaxes=set(xmin=0, xmax=πβout/2, ymin=-160, ymax=0),
labels=set(xaxis="Frequency [Hz]", yaxis="Uβ(π), W(π)"),
)
if typeof(input) in [SrcSine, SrcRaw]
N = length(u0)
local U
if isa(input, SrcSine)
U = fft(u0)/(N/4)
else #SrcRaw
U = fft(applywnd(u0, ds_hann(N)))/(N/4)
end
π = range(0, stop=πβout, length=N+1); π = π[1:div(N,2)+1]
UdB = dbv.(U[keys(π)])
UdBw = waveform(π, UdB)
push!(plot_πdec,
cons(:wfrm, UdBw, line=set(style=:solid, color=:blue, width=2), label="Uβ"),
)
end
#Plot ΞΞ£ signals (time domain):
Nplot = 300 #Number of samples to plot
simresult = simulateDSM(u, m.sys)
v = simresult.v; y = simresult.y
q = v - y #Quantization error; assumes quantizer gain = 1.
N = length(v)
n = 1:N
if N>Nplot #Sample values from the middle:
n = floor.(Int, N/2-Nplot/2:N/2+Nplot/2-1)
end
plot_tos = cons(:plot, linlin, title="Signals @ Modulator Input/Output", legend=false,
xyaxes=set(ymin=-1.2, ymax=1.2),
labels=set(xaxis="samples", yaxis="v(t)"),
)
_x = collect(0:length(n)-1)
vw = wfrm_stairs(_x, v[n])
uw = waveform(_x, u[n])
push!(plot_tos,
cons(:wfrm, vw, line=set(style=:solid, color=:blue, width=2), label="v"),
cons(:wfrm, uw, line=set(style=:solid, color=:green, width=2), label="u"),
)
#Plot V(π), from 0 to πββ/2. Use the middle Nfft points of v
N = length(v)
Nfft = min(N, 16*8192)
n = array_round((N-Nfft)/2+1):array_round((N+Nfft)/2)
V = fft(applywnd(v[n], ds_hann(Nfft)))/(Nfft/4)
inBin = ceil(Int, Nfft/1000)
if isa(input, SrcSine)
inBin = round(Int, input.f/fos*Nfft+1) #Bin of tone
end
(πnrm, Vp) = logsmooth(V, inBin)
plot_πos = cons(:plot, loglin, title="Spectrum (OSR=$OSR)", legend=false,
xyaxes=set(xmin=100, xmax=fos/2, ymin=-160, ymax=0),
labels=set(xaxis="Frequency [Hz]", yaxis="V(π)"),
)
Vpw = waveform(πnrm*fos, Vp)
nbw_str = @sprintf("NBW = %.1f Hz", fos*1.5/Nfft) #orig. coords: (Fs/2, -90); :cr
push!(plot_πos,
cons(:wfrm, Vpw, line=set(style=:solid, color=:blue, width=2), label="V"),
cons(:atext, nbw_str, y=-90, reloffset=set(x=0.95), align=:cr),
)
#Compute w
w = sinc_decimate(v, m.sincorder, m.OSR)
filtered_q = sinc_decimate(q, m.sincorder, m.OSR)
N = length(w)
t = collect(0:N-1)/πβout
ww = wfrm_stairs(t, w)
push!(plot_tdec,
cons(:wfrm, ww, line=set(style=:solid, color=:red, width=2), label="w"),
)
#Plot W(π), from 0 to πβout/2
if typeof(input) in [SrcSine, SrcRaw]
Nfft = length(w)
local W
if isa(input, SrcSine)
W = fft(w)/(N/4)
else
W = fft(applywnd(w, ds_hann(N)))/(N/4)
end
π = range(0, stop=πβout, length=Nfft+1); π = π[1:div(Nfft,2)+1]
WdB = dbv.(W[keys(π)])
WdBw = waveform(π, WdB)
nbw_str = @sprintf("NBW = %.1f Hz", πβout*1.5/Nfft) #orig. coords: (10, -90); :cl
push!(plot_πdec,
cons(:wfrm, WdBw, line=set(style=:solid, color=:red, width=2), label="W"),
cons(:atext, nbw_str, y=-90, reloffset=set(x=0.05), align=:cl),
)
end
pcoll = push!(cons(:plot_collection, ncolumns=2),
plot_tdec, plot_πdec, plot_tos, plot_πos,
)
return (plot=pcoll, u0=u0, w=w, input=input)
end
end #module DSAudioDemo
#==Auto-run test code:
===============================================================================#
println()
display(@doc(DSAudioDemo)) #Show user how to use DSAudioDemo
function load_demo4_audio_data(m::DSAudioDemo.Modulator)
srcpath = dirname(pathof(RSDeltaSigmaPort))
fpath = joinpath(srcpath, "..", "original_source", "delsig", "dsdemo4.mat")
alldata = DSAudioDemo.MAT.matread(fpath)
ds = alldata["ds"][:]
sd = alldata["sd"][:]
u0 = ds
u = interp(sd, m.OSR)
return DSAudioDemo.SrcRaw(u, u0)
end
function playresults(results)
if isa(results.input, DSAudioDemo.SrcRamp)
@warn("Will not playback ramp signal.")
return
end
println()
@info("Listening to ideally sampled/decimated signal...")
flush(stdout); flush(stderr)
@show DSAudioDemo.play(results.u0)
println()
@info("Listening to modulator output...")
flush(stdout); flush(stderr)
@show DSAudioDemo.play(results.w)
println("\nReplay results with:")
println("\tplayresults(results)")
return
end
#Inputs
dsm = DSAudioDemo.Modulator(1, OSR=32, sincorder=2)
#sig = load_demo4_audio_data(dsm)
sig = DSAudioDemo.SrcSine(f=500) #500, 4000, 4200
#sig = DSAudioDemo.SrcRamp()
println("\nUsing modulator:")
@show dsm
println()
@info("Performing ΞΞ£ audio simulation..."); flush(stdout); flush(stderr)
results = DSAudioDemo.run(dsm, Tsim=3.0, input=sig)
println("\tdone."); flush(stdout); flush(stderr)
println()
@info("Displaying results..."); flush(stdout); flush(stderr)
displaygui(results.plot)
playresults(results)
println()
:END_OF_DEMO