11^{:kindly/hide-code true
2- :clay {:title " DSP Intro"
3- :quarto {:author []
4- :description " Starting the journey of DSP in Clojure."
5- :category :clojure
6- :type :post
7- :date " 2025-11-02"
8- :tags [:dsp :math :music ]
9- :draft true }}}
2+ :clay {:title " DSP Intro"
3+ :quarto {:author [:eugnes :daslu ]
4+ :description " Starting the journey of DSP in Clojure."
5+ :category :clojure
6+ :type :post
7+ :date " 2025-11-02"
8+ :tags [:dsp :math :music ]
9+ :draft true }}}
1010(ns signal-processing.intro
1111 (:require [scicloj.kindly.v4.kind :as kind]
1212 [tech.v3.datatype :as dtype]
1515 [tablecloth.api :as tc]
1616 [scicloj.tableplot.v1.plotly :as plotly]))
1717
18+ ; ; # Introduction to Digital Signal Processing
19+ ; ;
20+ ; ; Welcome! Let's explore how to generate and manipulate audio signals in Clojure.
21+ ; ; We'll start simple - creating a single tone - then build up to synthesizing
22+ ; ; a more complex sound like a violin.
23+ ; ;
24+ ; ; ## What is Digital Signal Processing?
25+ ; ;
26+ ; ; Sound waves are continuous vibrations in the air. To work with them on a computer,
27+ ; ; we need to **sample** them - take measurements at regular intervals. The **sample rate**
28+ ; ; tells us how many measurements per second. CD-quality audio uses 44,100 samples per second.
1829
1930(require '[scicloj.kindly.v4.kind :as kind]
2031 '[tech.v3.datatype :as dtype]
2334 '[tablecloth.api :as tc]
2435 '[scicloj.tableplot.v1.plotly :as plotly])
2536
26- (def sample-rate 44100.0 )
37+ ; ; ## Step 1: Creating Our First Sound Wave
38+ ; ;
39+ ; ; Let's start by generating a simple pure tone - a sine wave at 440 Hz (the note A4,
40+ ; ; which orchestras use for tuning). This is the foundation of all audio synthesis.
2741
42+ (def sample-rate 44100.0 )
2843
44+ ; ; Now let's create the actual waveform. We'll generate 10 seconds of audio:
45+ ; ; - Create a time axis from 0 to 10 seconds
46+ ; ; - Calculate the sine wave: amplitude × sin(2π × frequency × time)
47+ ; ; - Store everything in a dataset so we can plot and analyze it
2948
3049(def example-wave
3150 (let [duration 10
3251 num-samples (* duration sample-rate)
52+ ; ; Create a time vector: [0, 1/44100, 2/44100, ..., 10]
3353 time (dtype/make-reader :float32
3454 num-samples
3555 (/ idx sample-rate))
36- freq 440
37- amp 3800
56+ freq 440 ; ; A4 - the tuning note
57+ amp 3800 ; ; Amplitude (loudness)
58+ ; ; Generate the sine wave
3859 value (-> time
39- (dfn/* (* 2 Math/PI freq))
40- dfn/sin
41- (dfn/* amp))]
60+ (dfn/* (* 2 Math/PI freq)) ; ; Convert time to phase
61+ dfn/sin ; ; Calculate sine
62+ (dfn/* amp))] ; ; Scale by amplitude
4263 (tc/dataset {:time time
4364 :value value})))
4465
66+ ; ; Let's look at our dataset
4567example-wave
4668
69+ ; ; ## Visualizing the Wave
70+ ; ;
71+ ; ; Here are the first 200 samples (about 4.5 milliseconds of audio).
72+ ; ; You can see the smooth oscillation of the sine wave.
73+
4774(-> example-wave
4875 (tc/head 200 )
4976 (plotly/layer-line {:=x :time
5077 :=y :value }))
5178
79+ ; ; ## Hearing the Sound
80+ ; ;
81+ ; ; Seeing is believing, but hearing is more fun! Let's create an audio player
82+ ; ; that will render this waveform as actual sound in your browser.
83+
5284(defn audio [samples]
5385 (with-meta
5486 {:samples samples
5587 :sample-rate sample-rate}
5688 {:kind/audio true }))
5789
90+ ; ; Click play to hear the 440 Hz tone!
5891(-> example-wave
5992 :value
6093 audio)
6194
95+ ; ; ## Step 2: Creating Complex Sounds - Violin Synthesis
96+ ; ;
97+ ; ; A pure sine wave sounds very artificial - like an old telephone tone.
98+ ; ; Real instruments are rich and complex because they produce many frequencies at once.
99+ ; ;
100+ ; ; The secret? **Additive synthesis** - combining multiple sine waves (called harmonics).
101+ ; ; A violin playing A4 doesn't just produce 440 Hz - it produces 440 Hz plus harmonics
102+ ; ; at 880 Hz, 1320 Hz, 1760 Hz, 2200 Hz, and more. Each harmonic has its own amplitude.
103+ ; ;
104+ ; ; Here are the main frequency components that give a violin its characteristic sound:
105+
62106(def violin-components
63- [[:A4 440 3800 ]
64- [:A5 880 2750 ]
65- [:E6 1320 600 ]
66- [:A6 1760 700 ]
67- [:C#7 2200 1900 ]])
107+ [[:A4 440 3800 ] ; ; Fundamental (the note we hear)
108+ [:A5 880 2750 ] ; ; 2nd harmonic (octave above)
109+ [:E6 1320 600 ] ; ; 3rd harmonic
110+ [:A6 1760 700 ] ; ; 4th harmonic
111+ [:C#7 2200 1900 ]]); ; 5th harmonic
112+
113+ ; ; Now let's generate all five harmonics as separate columns in a dataset.
114+ ; ; Each harmonic is a sine wave at its own frequency and amplitude.
68115
69116(def violin-components-dataset
70117 (let [duration 10
@@ -73,6 +120,7 @@ example-wave
73120 num-samples
74121 (/ idx sample-rate))]
75122 (->> violin-components
123+ ; ; For each [label frequency amplitude] triple, generate a sine wave
76124 (map (fn [[label freq amp]]
77125 [label (-> time
78126 (dfn/* (* 2 math/PI freq))
@@ -81,17 +129,25 @@ example-wave
81129 (into {:time time})
82130 tc/dataset)))
83131
132+ ; ; Our dataset now has columns: :time, :A4, :A5, :E6, :A6, :C#7
84133violin-components-dataset
85134
135+ ; ; Let's visualize just the fundamental (A4) first:
136+
86137(-> violin-components-dataset
87138 (tc/head 200 )
88139 (plotly/layer-line {:=x :time
89140 :=y :A4 }))
90141
142+ ; ; Now let's see all five harmonics together. First, we'll reshape the data
143+ ; ; from wide format (one column per harmonic) to long format (easier to plot):
144+
91145(-> violin-components-dataset
92146 (tc/head 200 )
93147 (tc/pivot->longer (complement #{:time })))
94148
149+ ; ; Plot all harmonics on the same chart, colored by frequency.
150+ ; ; Notice how the higher harmonics oscillate faster!
95151
96152(-> violin-components-dataset
97153 (tc/head 200 )
@@ -101,25 +157,56 @@ violin-components-dataset
101157 :=y :value
102158 :=color :$column}))
103159
160+ ; ; ## Step 3: Combining the Harmonics
161+ ; ;
162+ ; ; Now for the magic moment! When we add all five sine waves together,
163+ ; ; we get a complex waveform that sounds like a violin. This is the essence
164+ ; ; of additive synthesis - rich sounds emerge from simple building blocks.
165+
104166(def violin-dataset
105167 (-> violin-components-dataset
106168 (tc/add-column :violin
169+ ; ; Add all five harmonics together
107170 #(dfn/+ (:A4 %)
108171 (:A5 %)
109172 (:E6 %)
110173 (:A6 %)
111174 (:C#7 %)))))
112175
176+ ; ; Look at the combined waveform - it's much more complex than a pure sine wave!
177+ ; ; The shape repeats at 440 Hz (the fundamental), but with rich texture from the harmonics.
178+
113179(-> violin-dataset
114180 (tc/head 200 )
115181 (plotly/layer-line {:=x :time
116182 :=y :violin }))
117183
184+ ; ; ## Listen to the Violin Sound
185+ ; ;
186+ ; ; This is the payoff! Compare this to the pure tone we heard earlier.
187+ ; ; The violin sound is warmer and more musical because of the harmonics.
188+ ; ; (We divide by 7000 to normalize the amplitude after adding the components)
189+
118190(-> violin-dataset
119191 :violin
120192 (dfn// 7000.0 )
121193 audio)
122194
195+ ; ; ## What's Next?
196+ ; ;
197+ ; ; You've now learned the fundamentals of digital signal processing:
198+ ; ; - Sampling and sample rates
199+ ; ; - Generating sine waves
200+ ; ; - Additive synthesis with harmonics
201+ ; ;
202+ ; ; From here, you could explore:
203+ ; ; - Envelopes (attack, decay, sustain, release) to shape sounds over time
204+ ; ; - Filters (low-pass, high-pass) to sculpt frequency content
205+ ; ; - Modulation (vibrato, tremolo) for expressive effects
206+ ; ; - The Fourier transform to analyze existing sounds
207+ ; ;
208+ ; ; Happy signal processing!
209+
123210
124211
125212
0 commit comments