DSP Study Group - Reading audio data from WAV-files

Exploring WAV-files for DSP in Clojure.
Published

November 13, 2025

Exploration from the Scicloj DSP Study Group Second meeting - Nov. 08th 2025 and some follow-up investigation

Welcome! These are notes from our second study group session, where we’re learning digital signal processing together using Clojure. We’re following the excellent book Think DSP by Allen B. Downey (available free online).

Huge thanks to Professor Downey for writing such an accessible and free introduction to DSP, and for sharing with us the work-in-progress notebooks of Think DSP 2.

Along with this study group came the idea to have an online creative coding festival around Clojure in the first months of 2026. In this meeting we spent some time brainstorming on how that might look and what the scope could be. The remaining time of the session we looked into downloading and reading WAV-files in Clojure.

Why WAV Files?

The notebooks in Think DSP 2 work with WAV files loaded from GitHub as a basis for further processing, so we need a way to load these as well. After obtaining the file, we need to get at the audio data it contains.

Simplified WAV Format

First, let’s take a superficial look at what data WAV files contain, before we dive into getting the data. A simple WAV file consists of a header and pure audio data following it. There are several iterations on specifications for the WAV format and the format allows for quite some flexibility in placing different metadata in the file, as well as different encodings.

--- config: theme: 'forest' --- block columns 1 block:wav columns 5 block:HeaderId columns 1 HeaderLabel["Header"] end block:F1 columns 1 FrameLabel1["Frame"] end block:F2 columns 1 FrameLabel2["Frame"] end block:F3 columns 1 FrameLabel3["Frame"] end block:FN columns 1 FrameLabelN["..."] end end

The WAV (Waveform Audio File Format) file format is a RIFF (Resource Interchange File Format) file which stores data in chunks. Each chunk consists of a tag and data. Lets consider a partial example, which corresponds to the way the WAV file we want to read is arranged:

--- config: theme: 'forest' --- block columns 1 block:wav columns 3 block:HeaderId columns 1 HeaderLine1["RIFF"] HeaderLine2["WAVE"] end block:HeaderId2 columns 1 HeaderLine3["fmt "] HeaderLine4["1"] HeaderLine5["44100"] HeaderLine6["16"] end block:data columns 1 DataLabel["data"] ChanF1["ch0"] ChanF2["ch0"] ChanF2["ch0"] ChanF3["ch0"] ChanFN["..."] end end

The header comprises of the tag RIFF, its chunk tagged with the specific format WAVE and a subchunk fmt ⁠, which describes the contained audio data. This represents some of the header information in a WAV file with a single, 16-bit mono sound channel and 44.100 samples per second.

As we learned in the first session of the DSP study group:

Sound waves are continuous vibrations in the air. To work with them on a computer, we need to sample them - take measurements at regular intervals. The sample rate tells us how many measurements per second. CD-quality audio uses 44,100 samples per second.

These samples are stored in the WAV files data tagged subchunk. Since this is mono sound, there is one frame with one channel per sample. For multiple channels, each frame consists of all channels and their respective sample.

Libraries We’re Using

  • Kindly - Visualization protocol that renders our data as interactive HTML elements (through Clay)
  • dtype-next - Efficient numerical arrays and vectorized operations (like NumPy for Clojure)
  • Tablecloth - DataFrame library for data manipulation and transformation
  • Tableplot - Declarative plotting library built on Plotly
  • javax.sound.sampled - Some classes from the Java standard libraries sound package to read WAV Files.
(require '[scicloj.kindly.v4.kind :as kind]
         '[clojure.java.io :as io]
         '[tech.v3.datatype.functional :as dfn]
         '[tablecloth.api :as tc]
         '[scicloj.tableplot.v1.plotly :as plotly])
(import '(javax.sound.sampled AudioFileFormat
                              AudioInputStream
                              AudioSystem)
        '(java.io InputStream)
        '(java.nio ByteBuffer
                   ByteOrder))

Downloading a WAV File

(defn copy [uri file]
  (with-open [in (io/input-stream uri)
              out (io/output-stream file)]
    (io/copy in out)))
(copy tuning-fork-url tuning-fork-path)
Importanthttps://github.com/AllenDowney/ThinkDSP/raw/master/code/18871__zippi1__sound-bell-440hz.wav
                                               clojure.core/eval                     core.clj: 3232
                                                             ...                                  
                                        dsp.wav-files/eval159546                   REPL Input:     
                                              dsp.wav-files/copy                   REPL Input:     
                                                             ...                                  
                                    clojure.java.io/input-stream                       io.clj:  124
                                    clojure.java.io/input-stream                       io.clj:  139
                                            clojure.java.io/fn/G                       io.clj:   72
                                              clojure.java.io/fn                       io.clj:  259
                                            clojure.java.io/fn/G                       io.clj:   72
                                              clojure.java.io/fn                       io.clj:  242
                                         java.net.URL.openStream                     URL.java: 1325
sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream  HttpsURLConnectionImpl.java:  223
      sun.net.www.protocol.http.HttpURLConnection.getInputStream       HttpURLConnection.java: 1632
     sun.net.www.protocol.http.HttpURLConnection.getInputStream0       HttpURLConnection.java: 2030
java.io.FileNotFoundException: https://github.com/AllenDowney/ThinkDSP/raw/master/code/18871__zippi1__sound-bell-440hz.wav

source: src/dsp/wav_files.clj