Sardine is written and executes in python. You can use python code both inside and outside of Players and @swim functions. This is one of most powerful features of Sardine - once you understand how to use it. There are also times where you need to execute python commands.
Importing modules is a standard way to access additional functionality in python. In Sardine, importing from the random module is needed whenever you want random values in your expressions or patterns. In the example there are two calls to random functions:
randint(100 ,400) and
random() generates a float between 0 and 1. Multipling this by 1.5 scales the random range out, and +0.4 moves it up, to avoid speeds that are too low. This yields random values between
0.4 and 1.9.
from random import * clock.tempo=90 @swim def demo(p=1, i=0): D('electro1:2 electro1:4 electro1:3 feelfx:2', freq=randint(100,400), speed=(random() * 1.5) + 0.4, i=i) again(demo, p=0.5, i=i+1)
Looking for a way to reduce keystrokes? Aliases are simple. This example uses a python assignment with a as an alias for "again."
a=again @swim def demo(p=1, i=0): D('electro1:2 electro1:4 electro1:3 feelfx:2', i=i) a(demo, p=0.5, i=i+1)
Calling python functions
You can call python functions from within a @swim. The @swim below uses a Pattern Object to gradually increase and decrease the clock.tempo value. A python print() statement shows the value of clock.tempo as it is changing.
clock.tempo=90 @swim def clockPat(p=1, i=0): print(f"clock.tempo: ", clock.tempo) # python print function clock.tempo=P('[90:180,2][180:90,4]', i) # Sardine Pattern Object again(clockPat, p=1, i=i+1)
Generating values dynamically w custom functions
This shows a simple substitution. A variable is created with a string of note values.
seq = '40 51 62 72' Pa * d('supersaw', n=seq)
Now instead of just setting fixed values, we write a short custom function to generate note values. Load the
lowHigh() function first. Then start the Pa player. The same two notes will continue to play until you execute the Player again. This will cause it to make another function call to
def lowHigh(): low = str(randint(30, 50)) high = str(randint(50, 70)) notes = low + ' ' + high return notes Pa * d('supersaw', n=lowHigh()) # execute this line again to change note values
Custom functions can also be called within @swim, with an important difference. Here load the
lowHigh() custom function then start the @swim. When referenced within the @swim, we get new note values every time!
def lowHigh(): low = str(randint(30, 50)) high = str(randint(50, 70)) notes = low + ' ' + high return notes @swim def melody(p=1, i=0): D('supersaw', midinote=lowHigh(), i=i) again(melody, p=1, i=i+1)
Setting amphibian variables
A similar results can be achieved using Amphibian Variables.
Amphibian variables can have their values set outside of @swim with a python assignment/expression, or with a custom function.
The custom function below returns a list of midi note value with an argument that sets the number of notes in the list.
def randNotes(listLen): notes =  i = 0 while i < listLen: notes.append(randint(30,80)) i += 1 return notes V.n = randNotes(5) # Execute again to change notes Pa * d('supersaw', n='(v n)', p=1 )
For more info see Amphibian Variables.
This example uses python
if/elif/else conditional logic to switch between sample sets and change the tempo. Notice the use of the iterator and how resetting it to 0 at the end resets the conditional logic.
@swim def demo(p=1, i=0): print(f"i = ", i) if (i < 8): clock.tempo = 60 D('electro1:2 electro1:4 electro1:3 feelfx:2', i=i) elif (i < 16): D('east:0~8', i=i) clock.tempo = 120 else: i = 0 again(demo, p=0.5, i=i+1)
Going even deeper
See the next section - Advanced python: Sample Slicer, where custom functions generate 4 Amphibian Variables at once.