Skip to content

✨ soir

soir

Soir is a Python library for live coding music. It provides facilities to create and manipulate audio tracks, and to interact with external synthesizers. There are two important concepts in Soir:

  • Live functions (@live decorator) that are executed each time the code is changed. They are used to setup the environment, and to create tracks and instruments.

  • Loops functions (@loop decorator) that are rescheduled every given number of beats. They are used to create patterns and sequences.

# Live function
@live
def setup():
    bpm.set(120)

# Loop function
@loop(beats=1)
def kick():
    log('beat')

Soir's facilities are organized in modules that are accessible from the global context. For example, to set the BPM, you can use bpm.set(120) without having to explicitly import the bpm module. The available modules are:

Cookbook

Minimalistic Example

@live
def setup():
    bpm.set(120)

    tracks.setup([
        tracks.mk_sampler(1, muted=False, volume=100),
    ])

s = sampler.new('passage')

@loop(beats=4, track=1)
def kick():
    for i in range(4):
        s.play('kick')
        sleep(1)

Reference

live()

Decorator to create a live function that is executed each time the code is changed.

@live
def setup:
  tracks.setup([
    tracks.mk("sampler", 1, muted=False, volume=100),
  ])

Returns:

Type Description
callable

A decorator registering and executing the live function.

log(message)

Log a message to the console.

This function is used to log messages to the console. It is useful for debugging purposes.

log(f'We are at beat {bpm.beat()}')

Parameters:

Name Type Description Default
message str

The message to log.

required

loop(beats=4, track=1, align=True)

Decorator to create a loop that is rescheduled every given number of beats.

The concept of a loop is similar to Sonic Pi's live loops. Code within a loop is executed using temporal recursion, and can be updated in real-time: the next run of the loop will execute the updated version. This provides a way to incrementally build audio performances by editing code. Loops should not be blocking as it would freeze the main thread. For this reason, blocking facilities such are sleep are provided by the engine.

@loop(beats=4, track=1)
def my_loop():
  log("Hello World")

Parameters:

Name Type Description Default
beats int

The duration of the loop in beats.

4
track int

The track to use.

1
align bool

Whether to align the loop on its next beat sequence.

True

Returns:

Type Description
callable

A decorator registering and scheduling the function in a loop.

sleep(beats)

Sleep for the given duration in beats in the current loop.

In this example, we define a 4-beats loop that plays a kick sample every beats, and sleeps for 1 beat between each sample.

@loop
def my_loop(beats=4, track=1):
   for i in range(4):
      s.play('kick')
      sleep(1)

Parameters:

Name Type Description Default
beats float

The duration to sleep in beats.

required

Raises:

Type Description
NotInLiveLoopException

If we are not in a loop.