3636(kind/audio
3737 {:src violin-file-name})
3838
39+ (kind/audio
40+ {:src (str " clojure_norway/meetup_2025_12/" violin-file-name)})
41+
3942; ; ## Reading the Wav file as data
4043
4144; ; [Reading WAV files](https://clojurecivitas.github.io/dsp/wav_files.html).
@@ -91,6 +94,8 @@ wav-format
9194 (with-open [wav-stream (io/input-stream violin-file-path)]
9295 (audio-data wav-stream)))
9396
97+ (require '[tech.v3.datatype :as dtype])
98+
9499(def wav-samples
95100 ; ; one of the two stereo channels
96101 (dfn// (->> wav-shorts
@@ -131,92 +136,96 @@ wav-ds
131136 (apply concat))
132137 :sample-rate sample-rate}
133138
134- ; ; ## Computing the power spectrum of the data
139+ ; ; ## Computing the Discrete Fouried Transform the data
135140
136141(import 'com.github.psambit9791.jdsp.transform.DiscreteFourier)
137142
138143(count wav-samples)
139144
140- (def some-part
141- (->> wav-samples
142- (drop sample-rate) ; drop a second
143- (take (* sample-rate 0.06 )) ; take 6% of a second
144- ))
145-
146- (def power-spectrum
147- (let [fft (-> some-part
148- double-array
149- DiscreteFourier.)]
150- (.transform fft)
151- ; ; Get magnitude spectrum (power)
152- (.getMagnitude fft true )))
153-
154- (count power-spectrum)
145+ (defn some-part [t0 t1]
146+ (dtype/sub-buffer
147+ wav-samples
148+ (int (* sample-rate t0))
149+ (int (* sample-rate (- t1 t0)))))
155150
156151(def Nyquist-freq (* 0.5 sample-rate))
157152
158- (def power-spectrum-ds
159- (tc/dataset
160- {:freq (dfn// (range )
161- (/ (count power-spectrum) Nyquist-freq))
162- :power power-spectrum}))
163-
164- (-> power-spectrum-ds
153+ (defn data->dft-ds [data]
154+ (let [dft (-> data
155+ double-array
156+ DiscreteFourier.)]
157+ (.transform dft)
158+ (let [amp (.getMagnitude dft true )
159+ phase (.getPhaseRad dft true )]
160+ (-> (tc/dataset {:freq (dfn// (range )
161+ (/ (count amp) Nyquist-freq))
162+ :amplitude amp
163+ :phase phase})
164+ (tc/add-column :power #(tcc/sq (:amplitude %)))))))
165+
166+ (def some-dft-ds
167+ (data->dft-ds
168+ (some-part 1 1.05 )))
169+
170+ (-> some-dft-ds
165171 (plotly/layer-line {:=x :freq
166172 :=y :power }))
167173
168- ; ; ## Finding peaks (WIP)
174+ ; ; ## Finding peaks
169175
170176(import 'com.github.psambit9791.jdsp.signal.peaks.FindPeak)
171177
172- (def n- peaks 10 )
173-
174- ( def peaks-ds
175- ( let [peaks ( -> power-spectrum
178+ (defn dft-ds-> peaks-ds [dft-ds { :keys [n-peaks]}]
179+ ( let [peaks ( -> dft-ds
180+ :power
181+ double-array
176182 FindPeak.
177183 .detectPeaks)]
178- (-> (tc/dataset {:freq (dfn// (.getPeaks peaks)
179- (/ (count power-spectrum) Nyquist-freq ))
180- :power (.getHeights peaks)
181- :prominence (.getProminence peaks)})
184+ (.getPeaks peaks)
185+ (-> dft-ds
186+ (tc/select-rows (dtype/as-reader
187+ (.getPeaks peaks)))
188+ (tc/add-column :prominence (.getProminence peaks))
182189 (tc/order-by [:prominence ] :desc )
183190 (tc/head n-peaks))))
184191
185- (-> power-spectrum -ds
192+ (-> some-dft -ds
186193 (plotly/base {:=x :freq
187194 :=y :power })
188195 plotly/layer-line
189- (plotly/layer-point {:=dataset peaks-ds}))
190-
196+ (plotly/layer-point {:=dataset ( dft-ds-> peaks-ds some-dft-ds
197+ { :n-peaks 10 })}))
191198
192199; ; ## Synthesizing from the peaks
193200
194201(require '[clojure.math :as math])
195- (require '[tech.v3.datatype :as dtype])
196202
197- (def components-dataset
198- (let [duration 1
199- num-samples (* duration sample-rate)
203+ (defn peaks-ds->components-ds [peaks-ds {:keys [duration]}]
204+ (let [num-samples (* duration sample-rate)
200205 time (dtype/make-reader :float32
201206 num-samples
202207 (/ idx sample-rate))]
203208 (->> (tc/rows peaks-ds :as-maps )
204209 ; ; For each peak, generate a sine wave
205- (map-indexed (fn [i {:keys [freq power]}]
210+ (map-indexed (fn [i {:keys [freq power phase ]}]
206211 [(str " wave" i)
207212 (-> time
208213 (dfn/* (* 2 math/PI freq))
209- dfn/sin
214+ ; ; (dfn/+ phase)
215+ dfn/cos
210216 (dfn/* power))]))
211217 (into {:time time})
212218 tc/dataset)))
213219
214- (-> components-dataset
215- (tc/head 200 )
216- (tc/pivot->longer (complement #{:time })))
220+ (-> some-dft-ds
221+ (dft-ds->peaks-ds {:n-peaks 10 })
222+ (peaks-ds->components-ds {:duration 1 })
223+ (tc/head 500 ))
217224
218- (-> components-dataset
219- (tc/head 200 )
225+ (-> some-dft-ds
226+ (dft-ds->peaks-ds {:n-peaks 10 })
227+ (peaks-ds->components-ds {:duration 1 })
228+ (tc/head 500 )
220229 (tc/pivot->longer (complement #{:time }))
221230 (tc/rename-columns {:$value :value })
222231 (plotly/layer-line {:=x :time
@@ -227,8 +236,8 @@ wav-ds
227236 (dfn// values
228237 (double (dfn/reduce-max (dfn/abs values)))))
229238
230- (def combined-dataset
231- (-> components-dataset
239+ (defn components-ds-> combined-ds [components-ds]
240+ (-> components-ds
232241 (tc/add-column :combined
233242 (fn [ds]
234243 (-> ds
@@ -237,8 +246,11 @@ wav-ds
237246 (->> (apply tcc/+))
238247 normalize)))))
239248
240- (-> combined-dataset
241- (tc/head 200 )
249+ (-> some-dft-ds
250+ (dft-ds->peaks-ds {:n-peaks 10 })
251+ (peaks-ds->components-ds {:duration 1 })
252+ components-ds->combined-ds
253+ (tc/head 500 )
242254 (plotly/layer-line {:=x :time
243255 :=y :combined }))
244256
@@ -248,22 +260,24 @@ wav-ds
248260 :sample-rate sample-rate}
249261 {:kind/audio true }))
250262
251- (-> some-part
263+ (-> ( some-part 1 1.05 )
252264 audio)
253265
254- (-> combined-dataset
266+ (-> some-dft-ds
267+ (dft-ds->peaks-ds {:n-peaks 10 })
268+ (peaks-ds->components-ds {:duration 1 })
269+ components-ds->combined-ds
255270 :combined
256271 audio)
257272
258273; ; ## Spectogram (WIP)
259274
260275(import 'com.github.psambit9791.jdsp.windows.Hanning)
261- (require '[tech.v3.dataset.tensor :as ds-tensor]
262- '[tech.v3.libs.buffered-image :as bufimg])
263276
264277
265- (def spectrogram
266- (let [window-size 0.1 ; seconds
278+
279+ (def stft
280+ (let [window-size 0.05 ; seconds
267281 window-samples (* sample-rate window-size)
268282
269283 ; ; Create Hanning window to reduce spectral leakage
@@ -298,36 +312,43 @@ wav-ds
298312 (fn [i j]
299313 (* (windows i j)
300314 (hanning i)))
301- :float32 )
302-
303- ; ; ;; Compute power spectrum for each window
304- spectrogram (-> hanninged-windows
305- ; ; Rearrange to [windows × time]
306- (tensor/transpose [1 0 ])
307- (tensor/slice 1 )
308- ; ; Apply FFT to each window
309- (->> (pmap (fn [window]
310- (let [fft (-> window
311- dtype/as-reader
312- double-array
313- DiscreteFourier.)]
314- (.transform fft)
315- ; ; Get magnitude spectrum (power)
316- (-> fft
317- (.getMagnitude true )
318- (dtype/as-reader )
319- (dtype/sub-buffer 0 2000 ))))))
320- tensor/->tensor
321- ; ; Reshape to [windows × frequencies]
322- (#(tensor/reshape %
323- [n-windows
324- (-> % first count)]))
325- ; ; Transpose to [frequencies × windows] for plotting
326- (tensor/transpose [1 0 ]))]
327-
328- spectrogram))
329-
330- (require '[clojure2d.color])
315+ :float32 )]
316+
317+ (-> hanninged-windows
318+ ; ; Rearrange to [windows × time]
319+ (tensor/transpose [1 0 ])
320+ (tensor/slice 1 )
321+ ; ; Apply FFT to each window
322+ (->> (pmap (fn [window]
323+ (let [dft (-> window
324+ dtype/as-reader
325+ double-array
326+ DiscreteFourier.)]
327+ (.transform dft)
328+ (let [amp (.getMagnitude dft true )
329+ phase (.getPhaseRad dft true )]
330+ (-> (tc/dataset {:freq (dfn// (range )
331+ (/ (count amp) Nyquist-freq))
332+ :amplitude amp
333+ :phase phase})
334+ (tc/add-column :power #(tcc/sq (:amplitude %))))))))))))
335+
336+
337+ (require '[tech.v3.dataset.tensor :as ds-tensor])
338+
339+ (def spectrogram
340+ (-> stft
341+ (->> (map :power ))
342+ tensor/->tensor
343+ ; ; Reshape to [windows × frequencies]
344+ (#(tensor/reshape %
345+ [(count %)
346+ (-> % first count)]))
347+ ; ; Transpose to [frequencies × windows] for plotting
348+ (tensor/transpose [1 0 ])))
349+
350+
351+ (require '[clojure2d.color :as color])
331352
332353(def palette
333354 (let [p (color/palette :viridis )]
@@ -338,6 +359,8 @@ wav-ds
338359(def normalized-spectrogram
339360 (normalize spectrogram))
340361
362+ (require '[tech.v3.libs.buffered-image :as bufimg])
363+
341364(-> (tensor/compute-tensor
342365 (conj (dtype/shape spectrogram) 3 )
343366 (fn [y x c]
@@ -346,4 +369,18 @@ wav-ds
346369 bufimg/tensor->image)
347370
348371
372+ ; ; ## Playing the spectrogram
373+
374+
375+
376+ (-> stft
377+ (->> (map (fn [dft]
378+ (-> dft
379+ (dft-ds->peaks-ds {:n-peaks 5 })
380+ (peaks-ds->components-ds {:duration 0.005 })
381+ components-ds->combined-ds
382+ (tc/select-columns [:combined ]))))
383+ (apply tc/concat))
384+ :combined
385+ audio)
349386
0 commit comments