|
8 | 8 | [tech.v3.tensor :as tensor] |
9 | 9 | [tablecloth.column.api :as tcc] |
10 | 10 | [fastmath.stats :as stats] |
11 | | - [tech.v3.datatype :as dtype])) |
| 11 | + [tech.v3.datatype :as dtype] |
| 12 | + [clojure.math :as math])) |
12 | 13 |
|
13 | 14 | ;; ## Data source |
14 | 15 | ;; |
@@ -223,8 +224,8 @@ wav-ds |
223 | 224 | :=color :$column})) |
224 | 225 |
|
225 | 226 | (defn normalize [values] |
226 | | - (tcc// values |
227 | | - (tcc/reduce-max (tcc/abs values)))) |
| 227 | + (dfn// values |
| 228 | + (double (dfn/reduce-max (dfn/abs values))))) |
228 | 229 |
|
229 | 230 | (def combined-dataset |
230 | 231 | (-> components-dataset |
@@ -257,83 +258,92 @@ wav-ds |
257 | 258 | ;; ## Spectogram (WIP) |
258 | 259 |
|
259 | 260 | (import 'com.github.psambit9791.jdsp.windows.Hanning) |
260 | | -(require '[tech.v3.dataset.tensor :as ds-tensor]) |
261 | | - |
262 | | -(let [window-size 0.03 ; seconds |
263 | | - window-samples (* sample-rate window-size) |
264 | | - |
265 | | - ;; Create Hanning window to reduce spectral leakage |
266 | | - hanning (-> window-samples |
267 | | - Hanning. |
268 | | - .getWindow |
269 | | - dtype/as-reader) |
270 | | - |
271 | | - ;; Overlap parameters |
272 | | - overlap-fraction 0.05 |
273 | | - hop (int (* overlap-fraction window-samples)) |
274 | | - |
275 | | - n-samples (count wav-samples) |
276 | | - |
277 | | - ;; Calculate how many windows we can extract |
278 | | - n-windows (-> n-samples |
279 | | - (- window-samples) |
280 | | - (quot hop)) |
281 | | - |
282 | | - ;; Create sliding windows: [time × windows × channels] |
283 | | - windows-shape [window-samples n-windows] |
284 | | - windows (tensor/compute-tensor |
285 | | - windows-shape |
286 | | - (fn [i j] |
287 | | - ;; Extract sample at time i, window j, channel k |
288 | | - (wav-samples (+ (* j hop) i))) |
289 | | - :float32) |
290 | | - |
291 | | - ;; Apply Hanning window to each segment |
292 | | - hanninged-windows (tensor/compute-tensor |
293 | | - windows-shape |
294 | | - (fn [i j] |
295 | | - (* (windows i j) |
296 | | - (hanning i))) |
297 | | - :float32) |
298 | | - |
299 | | - ;; ;; Compute power spectrum for each window |
300 | | - spectogram (-> hanninged-windows |
301 | | - ;; Rearrange to [windows × time] |
302 | | - (tensor/transpose [1 0]) |
303 | | - (tensor/slice 1) |
304 | | - ;; Apply FFT to each window |
305 | | - (->> (pmap (fn [window] |
306 | | - (let [fft (-> window |
307 | | - dtype/as-reader |
308 | | - double-array |
309 | | - DiscreteFourier.)] |
310 | | - (.transform fft) |
311 | | - ;; Get magnitude spectrum (power) |
312 | | - (.getMagnitude fft true))))) |
313 | | - tensor/->tensor |
314 | | - ;; Reshape to [windows × frequencies] |
315 | | - (#(tensor/reshape % |
316 | | - [n-windows |
317 | | - (-> % first count)])) |
318 | | - ;; Transpose to [frequencies × windows] for plotting |
319 | | - (tensor/transpose [1 0]))] |
320 | | - ;; (-> windows |
321 | | - ;; (dfn/* 100) |
322 | | - ;; plotly/imshow) |
323 | | - |
324 | | - ;; (-> hanninged-windows |
325 | | - ;; (dfn/* 100) |
326 | | - ;; plotly/imshow) |
327 | | - |
328 | | - (-> spectogram |
329 | | - (#(tensor/broadcast % (cons 3 (dtype/shape %)))) |
330 | | - (dfn/* 50) |
331 | | - (tensor/transpose [1 2 0]) |
332 | | - plotly/imshow)) |
333 | | - |
334 | | - |
335 | | - |
336 | | - |
| 261 | +(require '[tech.v3.dataset.tensor :as ds-tensor] |
| 262 | + '[tech.v3.libs.buffered-image :as bufimg]) |
| 263 | + |
| 264 | + |
| 265 | +(def spectrogram |
| 266 | + (let [window-size 0.1 ; seconds |
| 267 | + window-samples (* sample-rate window-size) |
| 268 | + |
| 269 | + ;; Create Hanning window to reduce spectral leakage |
| 270 | + hanning (-> window-samples |
| 271 | + Hanning. |
| 272 | + .getWindow |
| 273 | + dtype/as-reader) |
| 274 | + |
| 275 | + ;; Overlap parameters |
| 276 | + overlap-fraction 0.1 |
| 277 | + hop (int (* overlap-fraction window-samples)) |
| 278 | + |
| 279 | + n-samples (count wav-samples) |
| 280 | + |
| 281 | + ;; Calculate how many windows we can extract |
| 282 | + n-windows (-> n-samples |
| 283 | + (- window-samples) |
| 284 | + (quot hop)) |
| 285 | + |
| 286 | + ;; Create sliding windows: [time × windows × channels] |
| 287 | + windows-shape [window-samples n-windows] |
| 288 | + windows (tensor/compute-tensor |
| 289 | + windows-shape |
| 290 | + (fn [i j] |
| 291 | + ;; Extract sample at time i, window j, channel k |
| 292 | + (wav-samples (+ (* j hop) i))) |
| 293 | + :float32) |
| 294 | + |
| 295 | + ;; Apply Hanning window to each segment |
| 296 | + hanninged-windows (tensor/compute-tensor |
| 297 | + windows-shape |
| 298 | + (fn [i j] |
| 299 | + (* (windows i j) |
| 300 | + (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]) |
| 331 | + |
| 332 | +(def palette |
| 333 | + (let [p (color/palette :viridis)] |
| 334 | + (memoize |
| 335 | + (fn [ratio] |
| 336 | + (p (int (math/round (* ratio (dec (count p)))))))))) |
| 337 | + |
| 338 | +(def normalized-spectrogram |
| 339 | + (normalize spectrogram)) |
| 340 | + |
| 341 | +(-> (tensor/compute-tensor |
| 342 | + (conj (dtype/shape spectrogram) 3) |
| 343 | + (fn [y x c] |
| 344 | + ((palette (normalized-spectrogram y x)) |
| 345 | + c))) |
| 346 | + bufimg/tensor->image) |
337 | 347 |
|
338 | 348 |
|
339 | 349 |
|
0 commit comments