"This is my assistant, the awful DYNNE," said Dr. Dischord.
"You must forgive his appearance, for he really doesn't have any."
From The Phantom Tollbooth, by Norton Juster
A library for working with audio data. Supports manipulating, playing,
visualizing, and saving sounds. Sounds can be read in .wav and .mp3
formats, and saved in .wav format.
Available on Clojars. Put
this in your
The basic concept in dynne is the sound. A sound is the combination
of a duration, a number of channels, and a deferred way to get the
amplitudes of all the channels. There is a protocol,
dynne.sampled-sound/SampledSound that gives these capabilities
chunks returns a Clojure seq of chunks. A chunk is a seq
of Java primitive double arrays, one per channel. All arrays in the
chunk will be the same length. Amplitudes are represented as
double-precision floating point numbers, which will be clipped to -1.0
and 1.0 (inclusive) when a sound is saved (via save ).
Constructors for sounds are given for WAV and MP3 files
( read-sound ), and for arbitrary functions ( fn-sound ).
Processor functions accept one or more sounds and return a new sound.
Combination is by functional composition, so combining two sounds does
not result in the underlying sound being sampled - computation of the
combined operation is deferred until the combined sound is sampled.
(require '[dynne.sampled-sound :refer :all])
;; Or, to use the API based on core.async
;; (require '[dynne.async-sound :refer :all])
;; Create a simple one-second, 440 Hz sine wave sound
(def s (sinusoid 1.0 440))
;; See what it looks like
;; See what the first 0.01 seconds look like
(visualize (trim s 0 0.01))
;; Play it. Maybe turn down your volume a bit first. :)
;; Define a new sound that fades `s` in over 0.5 seconds
(def s2 (fade-in s 0.5))
;; Visualize that
;; Get the double array holding the raw amplitude data of the first
;; channel of the first chunk. Note that we have to pass a smaple
;; rate. File-based sounds will be converted to this rate.
(ffirst (chunks s2 16000))
;; => #<double [D@53b7f3b2>
;; Build up a more complicated sound
(def l (-> (sinusoid 3.0 440)
(def r (-> (square-wave 3.0 880)
(def s3 (-> (->stereo l r)
;; And play it
;; And save it as a 44.1 KHz WAV
(save s3 "sample.wav" 44100)
;; Load it back in
(def s4 (read-sound "sample.wav"))
;; read-sound also works with MP3 files, but we can only save to WAV
;; Make a sound of our own design: two seconds of stereo white noise
(def s5 (fn-sound 2.0 2 (fn ^double [^long c ^double t] (- (rand 2.0) 1.0))))
;; Play it too
(play (gain s5 0.1))
A moderate performance improvement in
Introduces a new namespace,
dynne.async-sound, which has sounds
generate chunks in separate threads, with handoff between threads via
core.async. This should
result in performance improvements for larger audio streams on systems
with multiple cores.
The new namespace is API-compatible with the
the only change required should be to switch to requiring the new
Major overhaul. Deprecate
dynne.sound namespace in favor of
dynne.sampled-sound, which uses a more efficient, chunked
representation of sounds leveraging Java native arrays for way higher
I produce the
Relevance podcast. We
talk about Clojure a lot, so I thought I would see if I could automate
the podcast production using Clojure. You can see the in-progress
product of that effort
here. Along the way, a
general-purpose audio processing library started to fall out of it. So
I busted it out into its own thing, which is now dynne.
Mainly because I started out just playing around with the idea and at
some point crossed into being completely obsessed with writing my own
thing. I never even looked at whether
Overtone can do what I need, because I
knew if I did, and it could, I would stop working on the problem, and
I was having a lot of fun.
In short: if Overtone does anything like this, you should use it. It
is likely to be much, much better than dynne.
Thanks to Stuart Sierra for helping
me with some of the weirder problems I hit, especially around compiler
Thanks to Zach Tellman for his
which was helpful in finding sub-optimal usages of numeric operations.
Thanks to Rich Hickey for his
suggestion to rewrite dynne in terms of sequences of double arrays,
which led to a massive increase in performance.
Thanks to Prismatic for their
hiphip library, which was really
helpful in writing the most out of the operations at the core of
Thanks to tommyettinger for his
fork of hiphip that
works around an issue with AOT.
Copyright © 2013 Craig Andera
Distributed under the Eclipse Public License, the same as Clojure.