Lisp Revelation

Algorithms are Thoughts, Chainsaws are Tools from Stephen Ramsay on Vimeo.

CreateDigitalMusic.com recently posted this video. Though the focus is on Live Coding, what jumped out was this comment on the syntax of Lisp:

“Rule number 1. Everything is a list. Rule number 2. Some lists are functions in which the first item of the list is a function name and the remaining items are parameters. You have now learned the entire syntax of Lisp.”

This is a bit of a revelation to me. Too be honest, I’ve sort of avoided lisp for the most superficial of reasons; There are just to many parenthesis.

And now that my mind is open again, I’m going to take a serious look into Lisp and especially Impromptu. I’m not saying that I’m shifting from a Python-based environment to a Lisp-based system, only that I will continue to remain open to other possibilities.

1 Comment link

Frequency Modulation

Modulation works. Barely. I modified the Sine class to accept objects of type UGen as input. Though I think it might be possible to modify class UGen so that all child classes automatically support modulation by default. That’s probably my next step.

Since only the Sine class supports modulation, what better way to test it than to generate some classic FM tones. I created @Instr SimpleFM, which is just that, a simple FM instrument with one modulator oscillator being fed into a carrier oscillator. Here’s the file: fm.py. And here’s the code:

from slipmat import *

@Instr
def SimpleFM(dur, amp, pch, env, index=1, ratio=1):
    freq = cpspch(pch)                            # Convert pch to Hz
    env_1 = RiseFall(dur, env) * UVal(index)      # Index envelope
    env_2 = RiseFall(dur, 0) * UVal(amp)          # Amplitude envelope
    m = Sine(env_1, freq * ratio)                 # Modulator
    c = Sine(env_2, UVal(freq) + UVal(freq) * m)  # Carrier
    return(c)

s = ScoreEvents()
s.event(0, 2, SimpleFM(2, 0.5, 8.03, 0.125, 4))
s.event(2, 2, SimpleFM(2, 0.5, 8.07, 0.5, 16, 2))
s.event(4, 4, SimpleFM(4, 0.3, 7.00, 0.125, 16, 3))
s.event(4, 2, SimpleFM(4, 0.3, 8.05, 0.5, 4))
s.event(6, 2, SimpleFM(2, 0.3, 8.03, 0.5, 4))
ScoreEventsToWave(s, "./fm.wav")

Listen at SoundCloud

Clearly, this audio example is no Stria.

17 Comments link

Slipmat Audio on SoundCloud

I just invested into a SoundCloud Lite account, which allows me to put up all the Slipmat audio examples online for our listening pleasure. Okay, so it’s going to be a lot of boring sine waves for awhile. Though I’ll sneak in the occasional interesting track from time to time.

If you look in the right hand column, you’ll see the Slipmat set play list. I’ll also embed individual tracks into blog posts from here on out.

Slipmat by jacobjoaquin

2 Comments link

Simple as Possible

Now that the Slipmat dev code is up at github, I can write smaller examples rather than cram everything into one large script. The example test_tone.py is written specifically to demonstrate the simplest possible script that generates audio. Here’s the code:

import slipmat

s = slipmat.ScoreEvents()
s.event(0, 4, slipmat.Sine())
slipmat.ScoreEventsToWave(s, "./test_tone.wav")

This produces a 4-second 440Hz sine tone, and writes it to a wave file.

Here’s the line-by-line run down. First, the slipmat module is imported, essentially turning Python into a synthesizer. Then a score object is created from class slipmat.ScoreEvents(); this is akin to a Csound score file, as it is responsible for scheduling events for unit generators and instruments. A slipmat.Sine() object is instantiated then scheduled to play for 4-seconds using the default frequency of 440Hz. Then slipmat.ScoreEventsToWave() parses the score and renders the audio, writing the results to the file “./test_tone.wav”.

This is how it looks today. As Slipmat changes and evolves, so will this example.

0 Comments link

_slipmat @ Twitter

If RSS isn’t your cup of tea, you can alternatively follow _slipmat @ twitter. All new blog posts, along with git commits (in digest form) are set to auto-aggregate to there. I’ll also post links and information related to slipmat, computer music and audio synthesis from time to time. And you’ll be able to send tweets my way as well.

0 Comments link

Stacking Time

By utilizing a Python list as a stack, it is possible to move the stylus to a specific time in the score, schedule events relative to the stylus, and then pop it back into its previous position. This is inspired by Processing functions pushMatrix() and popMatrix().

I’ll explain what I’m talking about through example. The following is an excerpt from the pushpop.py example in the Slipmat github repo:

# Measure 1
s.event(0, 0.25, Sine(1, cpspch(7.00)))
s.event(1, 0.25, Sine(1, cpspch(7.03)))
s.event(2, 0.25, Sine(1, cpspch(7.07)))
s.event(3, 0.25, Sine(1, cpspch(7.10)))

# Measure 2
s.time.append(4)
s.event(0, 0.25, Sine(1, cpspch(8.00)))
s.event(1, 0.25, Sine(1, cpspch(8.03)))
s.event(2, 0.25, Sine(1, cpspch(8.07)))
s.event(3, 0.25, Sine(1, cpspch(8.10)))
s.time.pop()

In measure 1, four Sine() events are created at times 0, 1, 2 and 3, specified in the first parameter of s.event().

In measure 2, before any calls to s.event() are made, the value 4 is append to a list object called “time,” part of class ScoreEvents. Until “time” is popped or appended, the value 4 is added to every start time in method event(). Measure 2 generates 4 events, created at times 4, 5, 6 and 7.

If these two measures were explicitly written, it would look like this:

s.event(0, 0.25, Sine(1, cpspch(7.00)))
s.event(1, 0.25, Sine(1, cpspch(7.03)))
s.event(2, 0.25, Sine(1, cpspch(7.07)))
s.event(3, 0.25, Sine(1, cpspch(7.10)))
s.event(4, 0.25, Sine(1, cpspch(8.00)))
s.event(5, 0.25, Sine(1, cpspch(8.03)))
s.event(6, 0.25, Sine(1, cpspch(8.07)))
s.event(7, 0.25, Sine(1, cpspch(8.10)))

The current position of the stylus is the combined sum of all values stored in list “time.” For example:

s.time.append(16)
s.time.append(30)
s.time.append(11)
s.event(7, 1, foo())
s.time.pop()
s.event(2, 1, bar())

The foo() event is scheduled at beat 64. That’s (16 + 30 + 11) for “time” plus an additional 7 for the first parameter of event(). After “time” is popped, bar() plays at beat 48; (16 + 30) for “time” plus 2 for the relative start of the event.

For the record, I think the s.event() code is a bit ugly, which I’m already addressing behind the scenes. If you’ve been keeping up with this blog, you might already have a good idea as to where I’m going with this.

0 Comments link

Slipmat at Github

Slipmat code has a new home at github. I have few things to get in order before I push the code to the new repository. Things should be up sometime week.

Just to be absolutely clear, the blog is staying here at noisepages.

0 Comments link

Lead-In Redux

I had three goals in mind in getting the first working Python Slipmat prototype, Lead-In, up and running:

  • Use pure Python, with no external modules.
  • Getting Python to behave like a real audio langauge.
  • Mold the syntax to feel like a real audio language.

I would say it’s a success on all three fronts, with plenty of room to grow.

However, there are some major issues. The biggest being that it’s slow; Amazingly slow. This was expected, as Python itself is an interpreted language, and not a DSP power house like C/C++. On my modern iMac, slipmat_lead-in.py took 1 minute and 19.681 seconds to render. I bet Csound would do a comparable job, in 1996.

So one of my first goals of the upcoming redesign is to make it just fast enough until a proper C/C++ engine can be written. I’m looking into NumPy/SciPy.

I’m also looking into Audiolab, “a python package to make noise with numpy arrays,” written David Cournapeau. This is, more or less, a Python wrapper module for Erik de Castro Lopo’s excellent libsndfile; I was already planning on using libsndfile, anyhow.

0 Comments link

Audio: Lead-In

Slipmat, now with audio.

Listen: Slipmat_Lead-In.mp3
Python Script: here

3 Comments link

Score Events

No computer music system is complete without the ability to place notes into a score/timeline. Read and download today’s script here.

The mechanisms that schedule score events and print the results are still a bit wonky. So I’m going to omit the explanation for now, and instead just focus on how they’re used. The following is the __main__ from the script:

if __name__ == "__main__":
    @Instr
    def RingTine(dur, amp, freq_1, freq_2):
        return Sine(amp, freq_1) * Sine(amp, freq_2) * RiseFall(dur, 0)

    s = ScoreEvents()
    s.event(0, 0.25, RingTine(0.25, 1, 440, 44))
    s.event(0.5, 0.125, RingTine(0.125, 0.333, 262, 44))
    s.event(0.75, 0.25, RingTine(0.25, 0.25, 262, 44))
    s.event(0.75, 0.25, RingTine(0.25, 0.5, 440, 44))
    for frame in PrintSamples(s): pass

The first part of __main__ defines an instrument called RingTine that generates a percussive ring-modulated timbre. At least in theory; Still no sound output.

The next step creates a ScoreEvents() object. This is where events are scheduled. This is followed by 4 lines of code that schedule events for @Instr RingTine with the ScoreEvents class method event(). The method takes three arguments:

ScoreEvents.event(start_time, duration, UnitGenerator(*args))

The very last line prints the samples generates by the score with the PrintSamples() iterator. The implementation is seriously lame at the moment, but is perfectly acceptable for a crude prototype.

0 Comments link
next