Skip to content

Commit e5d71a2

Browse files
committed
dsp intro - wording
1 parent ba5992e commit e5d71a2

File tree

1 file changed

+106
-19
lines changed

1 file changed

+106
-19
lines changed

src/signal_processing/intro.clj

Lines changed: 106 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
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]
@@ -15,6 +15,17 @@
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]
@@ -23,48 +34,84 @@
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
4567
example-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
84133
violin-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

Comments
 (0)