File: 05.5-Functions.md

package info (click to toggle)
sonic-pi 3.2.2~repack-8
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 71,872 kB
  • sloc: ruby: 30,548; cpp: 8,490; sh: 957; ansic: 461; erlang: 360; lisp: 141; makefile: 44
file content (130 lines) | stat: -rw-r--r-- 3,709 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
5.5 Functions

# Functions

Once you start writing lots of code, you may wish to find a way to
organise and structure things to make them tidier and easier to
understand. Functions are a very powerful way to do this. They give us
the ability to give a name to a bunch of code. Let's take a look.

## Defining functions

```
define :foo do
  play 50
  sleep 1
  play 55
  sleep 2
end
```

Here, we've defined a new function called `foo`. We do this with our old
friend the do/end block and the magic word `define` followed by the name
we wish to give to our function. We didn't have to call it `foo`, we could
have called it anything we want such as `bar`, `baz` or ideally
something meaningful to you like `main_section` or `lead_riff`.

Remember to prepend a colon `:` to the name of your function when you define
it.

## Calling functions

Once we have defined our function we can call it by just writing its
name:

```
define :foo do
  play 50
  sleep 1
  play 55
  sleep 0.5
end

foo

sleep 1

2.times do
  foo
end
```

We can even use `foo` inside iteration blocks or anywhere we may have
written `play` or `sample`. This gives us a great way to express
ourselves and to create new meaningful words for use in our compositions.

## Functions are remembered across runs

So far, every time you've pressed the Run button, Sonic Pi has started
from a completely blank slate. It knows nothing except for what is in
the buffer. You can't reference code in another buffer or another
thread. However, functions change that. When you define a function,
Sonic Pi *remembers* it. Let's try it. Delete all the code in your
buffer and replace it with:

```
foo
```

Press the Run button - and hear your function play. Where did the code
go? How did Sonic Pi know what to play? Sonic Pi just remembered your
function - so even after you deleted it from the buffer, it
remembered what you had typed. This behaviour only works with functions
created using `define` (and `defonce`).

## Parameterised functions

You might be interested in knowing that just like you can pass min and
max values to `rrand`, you can teach your functions to accept
arguments. Let's take a look:

```
define :my_player do |n|
  play n
end

my_player 80
sleep 0.5
my_player 90
```

This isn't very exciting, but it illustrates the point. We've created
our own version of `play` called `my_player` which is parameterised.

The parameters need to go after the `do` of the `define` do/end block,
surrounded by vertical goalposts `|` and separated by commas `,`. You
may use any words you want for the parameter names.

The magic happens inside the `define` do/end block. You may use the
parameter names as if they were real values. In this example I'm playing
note `n`.  You can consider the parameters as a kind of promise that
when the code runs, they will be replaced with actual values. You do
this by passing a parameter to the function when you call it. I do this
with `my_player 80` to play note 80. Inside the function definition, `n`
is now replaced with 80, so `play n` turns into `play 80`. When I call
it again with `my_player 90`, `n` is now replaced with 90, so `play n`
turns into `play 90`.

Let's see a more interesting example:

``` 
define :chord_player do |root, repeats| 
  repeats.times do
    play chord(root, :minor), release: 0.3
    sleep 0.5
  end
end

chord_player :e3, 2
sleep 0.5
chord_player :a3, 3
chord_player :g3, 4
sleep 0.5
chord_player :e3, 3
```

Here I used `repeats` as if it was a number in the line `repeats.times
do`. I also used `root` as if it was a note name in my call to `play`.

See how we're able to write something very expressive and easy to read
by moving a lot of our logic into a function!