Simple sound generation and processing library for Python
The purpose of tht library is to have a simple to use library to create and process sound files.
Dedicated langages alredy exists, but I was not 100% satisfied
with tools like cmusic
or nyquist
because they are too much
oriented toward music production (is this real?) and the learning
curve was steep.
Python already have a couple of libraries that somehow overlap
mysound
. But I was not too keen in requiring huge libraries installation
like numpy
and scipy
just to generate sine wave or extract the
envelope of a sound. Finally, a few other Python sound libraries
alredy exists, but they are more inclined toward playing or recording
sound--something outside of the primary goal of mysound
Least, and not last, A great motivation for that project was the fun factor!
I try to follow a gew guidelines while writing mysound
:
- imutability Most importantly, sound are stateless and immutable. There is no such thing as in-place transformations.
- functionl design As far as I can, I try to stick
a functional approach. Many parts of
mysound
are built around higher-order functions rather than OOP paradigme - speed is not a problem Well, this is self explanatory.
I don't target real time processing nor even fast processing.
mymusic
is written 100% in Python based on the assumption it will be fast enougth for the kind of task I need - memory is not a problem Even if I try to not waste memory and chase memory leaks, on the other hand, I do not hesitate to keep samples--even intermediate processing result-- in main memory as long as they can still be accessible.
- one size fits all Internally, all samples are stored and processed as double floating point numbers.
mysound
is designed around the concept of channel. A channel
is just a steam of samples encoded in floating point format (currently
float 32). Channels are not tight to any parameter coming from
their original source. So nothing prevents you from mixing channels
from sources at different sampling rate but no automatic resampling
will be performed. In the same spirit, you can easily achieve the
chipmunk effect by just sending a stream to a sink using a higher
sampling rate than the original stream source.
Some channels, like the silence
channel, produce an infinite stream
of samples. Other, notably those coming from an external source like
a file, can only produce a finite number of samples. When there
are no more samples to read, the stream is exhausted and subsequent
readings from the channel will produce an empty list of samples.
Some objects inherently works with multiple channels at the same time.
Take for example an interleaved stereo sound file. The library provides
the demux
to split a multichannel sources in a list of channels that
can be independently handled.
Symmetrically, the library provides the mux
function to create a multichannel
object. Muxing is mostly useful for sinks that need to work with
interleaved data.
Whereas single-channel sources produce an infinite steam of empty sample list
when exhausted, multi-channel sources produce an infinite stream of the Python
None
constant to ease end-of-steam detection. A multi-channel stream is
exhausted when at least one of its individual channels is exhausted.
A generator is a function that return a new channel. Generators can produce
a new channel from an external source like a file), or using a computational
method. silence
or noise
are examples of the later.
Note: Generators in mysound
are concepts related to Python's generators,
but they are implemented in a purely functional style, and not using
the standard generators infrastructure (yield
statements, and so on).
Generators should be idempotent. But this is sometimes hard to achieve, notably for generators based on random sources.
When a function create one (or several channels), based on already existing channel(s) we call it a processor rather than a generator
Processors are at the core of sound processing with mysound
. A processor
is a n-to-n function taking one or several channels and returning one or
several channels. Processors are stateless and their output is fully
defined by their input. As an example, the average
processor (which can
be used for stereo-to-mono conversion) will return a channel whose samples
are the average of the input sample.
Sinks are the counterpart of generators. They are function taking one or several input channels, and consuming the samples until at least one of the input stream is exhausted.
Thanks to the dynamic nature of Python, some actions can be performed
independently to channels and multichannels. For example, the skip
or trucate
action used to discard sample. You may think of actions as
higher-level utilities working similarly on 1-channel and multi-channel
streams.