- Bem vindo amigo :-)

Bem vindo ao Sonic Pi. Espero que você esteja tão animado para começar a fazer sons malucos quanto eu estou para te mostrá-los. Isso vai ser um passeio realmente legal onde você irá aprender tudo sobre música, síntese, programação, composição, performance e mais.

Mas espere, que rude da minha parte! Deixe eu me apresentar - Eu sou Sam Aaron - o cara que criou o Sonic Pi. Você pode me encontrar no Twitter @samaaron e estarei mais do que feliz em dizer um oi para você. Você também pode estar interessado em saber mais sobre minhas Live Coding Performances onde eu programo com o Sonic Pi ao vivo na frente do público.

Se tiver algum plano ou ideias para melhorar o Sonic Pi - por favor nos diga - feedback sempre ajuda. Nunca se sabe, sua ideia pode se tornar uma grande nova função!

Este tutorial é dividido em duas seções agrupadas por categorias. Escrevi isto para que se tenha uma fácil aprendizagem do início ao fim, sinta-se à vontade para acessar as seções que você se encaixa. Se sentir que algo está faltando, deixe-me saber e vou considerar isto para uma versão futura.

Finalizando, assistir outras pessoas programando é uma ótima maneira de se aprender. Eu faço regularmente transmissões ao vivo no http://youtube.com/samaaron então entre, me dê um oi e faça um monte de perguntas :-)

OK, então vamos começar…


1.1 - Programação ao vivo

Um dos aspectos mais excitantes de Sonic Pi, é a possibilidade de modificar a programação “ao vivo” para fazer música, assim como você toca uma guitarra. Isso significa, que com alguma prática você pode levar o Sonic Pi ao palco e apresentar-se.

Liberte sua mente

Antes de nós entrarmos em detalhes reais de como o Sonic Pi funciona no resto desse tutorial, eu gostaria de te dar uma experiência de como é programar ao vivo. Não se preocupe se você não entender muito (ou nada) do que verá. Só aperte os cintos e curta…

Repetição ao vivo

Vamos começar, copie o código a seguir em algum buffer vazio acima:

live_loop :flibble do
  sample :bd_haus, rate: 1
  sleep 0.5
end

Agora pressiona o botão Rodar e você ouvirá uma rápida batida de bateria tocando. Se a qualquer momento você quiser parar o som, basta pressionar o botão Parar. Mas calma, não pressione o botão ainda… Ao invés disso, siga os passos a segui:

Tenha certeza de que o bumbo continua rodando Altere o valor dormir para 0.5 para algo acima de 1. Pressione o botão Rodar novamente Note como a velocidade da batida foi alterada. Finalmente, lembre deste momento, essa é a primeira vez que você programou ao vivo com Sonic Pi e provavelmente não será a última vez…

Ok, isso foi bem simples. Vamos adicionar alguma outra coisa na mistura. Sobre sample :bd_haus adicione a linha sample: ambi_choir, rate: 0,3. Seu código deve estar assim agora:

live_loop :flibble do
  sample :ambi_choir, rate: 0.3
  sample :bd_haus, rate: 1
  sleep 1
end

Agora brinque um pouco. Altere os valores - o que acontece quando você usa valores maiores, ou valores menores ou negativos? Veja o que acontece quando você altera o valor rate: para o sample :ambi_choir apenas um pouco (digamos, para 0.29). O que acontece se escolher um valor bem pequeno para sleep? Veja se você pode fazer isso ficar tão rápido que seu computador irá parar com um erro porque ele não consegue acompanhar (se isso acontecer, apenas selecione um tempo maior pra sleep e pressione Rodar novamente).

Tente comentar uma das linhas de sample adicionando # no começo:

live_loop :flibble do
  sample :ambi_choir, rate: 0.3
#  sample :bd_haus, rate: 1
  sleep 1
end

Note como ele diz ao computador para ignorar essa linha, você não consegue ouvi-lá. Isto é chamado comentário. Em Sonic Pi você pode usar comentários para remover e adicionar coisas na mixagem.

Finalmente, vou deixar você com algo divertido para brincar. Pegue o código abaixo, e copie ele num buffer disponível. Agora, não tente entender isto além de perceber que nele há dois loops - então são duas coisas rodando ao mesmo tempo. Agora, faça aquilo que você faz de melhor - experimente e comece a brincar. Aqui vão algumas sugestões:

Tente alterar os valores azuis de rate: para ouvir a amostra de som mudar. Tente mudar os tempos sleep e escutar que ambos loops podem rodar em taxas diferentes. Tente descomentar a linha de exemplo (remova o #) e aprecie o som da guitarra tocada ao contrário. Tente trocar qualquer valor do mix: azul para números entre 0 (não na mixagem) e 1 (totalmente na mixagem).

Lembre-se de apertar Run e você irá ouvir a mudança na próxima vez que o loop rodar. Se você acabar num aperto, não se preocupe - aperte Stop, delete o código no buffer e cole uma cópia nova em folha e você estará pronto para tocar novamente. Fazer erros é a forma mais rápida de aprender…

live_loop :guit do
  with_fx :echo, mix: 0.3, phase: 0.25 do
    sample :guit_em9, rate: 0.5
  end
#  sample :guit_em9, rate: -0.5
  sleep 8
end
live_loop :boom do
  with_fx :reverb, room: 1 do
    sample :bd_boom, amp: 10, rate: 1
  end
  sleep 8
end

Agora,continue tocando e experimentando até que sua curiosidade sobre como tudo isso realmente surja e você comece a se perguntar oque mais você pode fazer com isso. Agora você está pronto para o resto do tutorial.

Então oque você está esperando…


1.2 - A Interface de Sonic Pi

Sonic Pi tem uma interface muito simples para codificar música. Vamos passar um tempinho explorando-a.

Interface do Sonic Pi

A - Controles de Reprodução B - Controles de Edição C - Ajuda e Informação D - Editor de Código E - Painel de Preferências F - Visualizador de Log G - Sistema de Ajuda H - Visualizador de Escopo I - Cue Viewer

Controles

Esses botões rosas são os controles principais para inciar e finalizar sons. Há um botão Run para executar o código no editor, Stop para parar todo o código que está sendo executado, Save para salvar todo o código para um arquivo externo e Record para criar uma gravação (um arquivo WAV) do som que está tocando.

B. Controles do Editor

These orange buttons allow you to manipulate the code editor. The Size + and Size - buttons allow you to make the text bigger and smaller.

C. Ajuda e Informação

Esses botões azuis te dão acesso à informação, ajuda e preferências. O botão Info irá abrir a janela de informações na qual contém informação sobre o Sonic Pi - o time fonte, histórico, contribuidores e comunidade. O botão Help ativa o sistema de ajuda (G) e o botão Prefs ativa a janela de preferências na qual te permite controlar alguns parâmetros básicos do sistema.

D. Editor de Código

Essa é a área em que você vai escrever seu código e compor/fazer música. É um simples editor de texto onde você pode escrever código,deleta-lo,recortar e colar, etc. Pense nele como uma versão muito básica do Word ou Google Docs. O editor vai colorir automaticamente as palavras baseado no seu significado no código. Isso parece estranho de primeira, mas logo você vai achar muito útil.Por exemplo, você saberá que algo é um número por que é azul.

E. Painel de Preferências

O Sonic Pi suporta várias preferências ajustáveis que podem ser acessadas alternando o botão prefs no conjunto de botões Informações e Ajuda. Isto irá alternara a visibilidade do Painel de Preferências que inclui várias opções que podem ser alteradas. Exemplos: forçar o modo mono, inverter stereo, alternar a verbosidade do log de saída e também um slider de volume e um seletor de áudio no Raspberry Pi.

F. Visualizador de logs

Quando você executa seu código, informações sobre o que o programa está fazendo serão exibidas no Visualizador do Log. Por padrão, você verá uma mensagem para cada som que você criou com o tempo exato em que o som foi disparado. Isto é muito útil para depurar seu código e entender o que seu código está fazendo.

G. Sistema de Ajuda

Uma das partes mais importantes da interface do Sonic Pi é o sistema de ajuda que aparece na parte de baixo da janela. Ela pode ser ligada e desligada clicando no botão azul Help. O sistema de ajuda contem ajuda e informações sobre todos os aspectos do Sonic Pi, incluindo este tutorial, uma lista de sintetizadores, samples, exemplos, efeitos e uma lista com todas as funções que o Sonic Pi oferece para programar música.

H. Visualizador de Onda

The visualizador de ondas permite que você veja o som que você está ouvindo. Você pode facilmente ver que a onda dente de serra parece com uma serra e que que o bip básico é uma curva senoidal. Você também pode ver a diferença entre sons intensos e calmos através dos tamanhos das linhas. Há 3 visualizações para trabalhar - a padrão mostra uma onda que combina os canais esquerdo e direito, há uma visualização stereo que mostra uma onda para cada canal. Finalmente há um visualizador de Curva de Lissajous que irá mostrar a relação de fase entre os canais esquerdo e direito e permite que você desenhe imagens bonitas com o som (https://pt.wikipedia.org/wiki/Curvas_de_Lissajous).

I. Cue Viewer

All internal and external events (called cues in Sonic Pi) are automatically logged in the Cue Viewer. For example, if you have a MIDI controller connected and press one of its buttons, you’ll see a new cue event in the Cue Viewer telling you the name of the controller and which button you pressed. Once you’ve mastered the basics of making and producing sounds, you’ll start to want to cue sounds or whole sections of sounds based on events such as these. A cue event is just an indication that something happened. For example, every time a live loop spins round, it sends a cue event out which is logged in the Cue Viewer. Also, external events such as MIDI messages from connected MIDI equipment and OSC messages from other programs or computers are also displayed in the Cue Viewer. It is also possible to directly generate cue events using the cue function. Anything that appears in the Cue Viewer can be used to trigger something happening. This is covered in more detail in Sections 10 to 12 of this tutorial.


1.3 - Aprenda tocando

O Sonic Pi te incentiva a aprender tanto sobre computação quanto sobre música através da ação de tocar e da experimentação. A coisa mais importante é que você esteja se divertindo e, antes que você perceba, você terá aprendido acidentalmente como programar, compor e tocar.

Não existem erros

Ainda sobre este assunto, deixe-me te dar um um conselho que eu aprendi ao longo dos meus anos de Live Coding com música - não existem erros, somente oportunidades. Isso é algo que ouvi bastante em relação ao jazz, mas funciona igualmente com Live Coding. Não importa o quão experiente você seja - desde um iniciante até um Algoraver experiente - as vezes você irá executar um código que irá gerar um resultado completamente inesperado, que irá soar insanamente legal - e neste, continue com ele. Entretanto, pode soar totalmente dissonante e fora de contexto. Não importa que isso aconteça - o que importa é que o que você irá fazer com isso. Pegue o som, malipule-o e transforme-o em algo maravilhoso. A multidão irá à loucura.

Comece de forma simples

Quando vocÊ está aprendendo, é tentador querer fazer coisas incríveis imediatamente. Porém, mantenha esse pensamento como um objetivo para ser alcançado mais tarde. Por enquanto, pense na coisa mais simples que você possa escrever que possa ser divertida e recompensadora. Este será o pequeno passo na direção das coisas incríveis que você tem na sua mente. Uma vez que você tenha uma ideia sobre este passo simples, tente e construa-o, brinque com ele e então veja que novas ideias isso te deu. Antes do que você perceba você estará ocupado se divertindo e fazendo progresso real.

Certifique-se de compartilhar seu trabalho com os outros!


2 - Sintetizadores

OK, chega de introduções - vamos fazer alguns sons.

Nesta seção cobriremos a base de disparar e manipular sintetizadores. Synth é uma abreviatura para sintetizador, que é uma palavra chique para algo que sintetiza (cria) sons. Tipicamente os sintetizadores são bastante complicados de usar - especialmente sintetizadores analógicos como módulos Eurorack interconectados por uma bagunça de fios. No entanto, o Sonic Pi te dá grande parte deste poder de uma forma muito simples e acessível.

Não se engane pela simplicidade imediata da interface do Sonic Pi. Você pode ir bem fundo na manipulação sofisticada de sons, se for este seu objetivo. Agora, segure-se…


2.1 - Seus Primeiros Beeps

Veja o código a seguir:

play 70

É aqui que tudo começa. Vá em frente, copie e cole este código no editor (o grande espaço em branco abaixo do botão Run). Agora pressione Run

Bip!

Intenso. Pressione outra vez. E outra vez. E outra vez

Wow, doido, eu tenho certeza de que você poderia fazer isso o dia todo. Mas espere, antes de se perder em uma corrente infinita de bips, tente modificar o número:

play 75

Consegue notar a diferença? Tente outro número:

play 60

So, lower numbers make lower pitched beeps and higher numbers make higher pitched beeps. Just like on a piano, the keys at the lower part of the piano (the left hand side) play lower notes and the keys on the higher part of the piano (the right hand side) play higher notes.

play 60

Não se preocupe se isso não significa nada pra você - Não significava para mim quando eu comecei. Tudo o que importa agora é que você saiba que números baixos criam bips graves e números altos criam bips mais agudos.

Acordes

Tocar uma nota pode ser divertido, mas tocar várias ao mesmo tempo pode ser ainda melhor. Tente:

play 72
play 75
play 79

Jazzy! Quando você escreve múltiplos plays, ele tocam todos ao mesmo tempo. Tente você mesmo - quais números soam bem juntos? Quais soam terríveis? Experimente, explore e encontre você mesmo.

Melodia

Tocar notas e acorde é divertido - mas e quanto uma melodia? E se você quiser tocar uma nota após a outra e não ao mesmo tempo? Bom, isso é fácil, você só precisa usar sleep (dormir/esperar) entre as notas:

play 72
sleep 1
play 75
sleep 1
play 79

Que belo arpejo. Mas o que o 1 significa em sleep 1? Bom, significa a duração da espera (sleep). Na realidade, significa espere durante uma batida, mas por enquanto vamos considerar como aguardar por 1 segundo. E se nós desejarmos tocar nosso arpejo um pouco mais rápido? Nós precisamos usar valores de espera menores. Que tal a metade? Isto é: 0.5:

play 72
sleep 0.5
play 75
sleep 0.5
play 79

Note como toca mais rápido. Agora tente você mesmo, mude os valores - use tempos e notas diferentes.

Uma coisa que você pode tentar são notas ‘intermediárias’, como play 52.3 e play 52.63. Não há absolutamente necessidade alguma para nos fixarmos às notas padrão. Toque e divirta-se.

Nomes tradicionais das notas

Para aqueles que já conhecem um pouco de notação musical (não se preocupe se você não souber - isso não é necessário para se divertir) você pode querer escrever uma melodia usando nomes das notas como C e F# (notação inglesa) em vez de números. O Sonic Pi entende isso. Você pode fazer o seguinte:

# Notação Inglesa: C=Dó; D=Ré; E=Mi; F=Fá; G=Sol; A=Lá; B=Si  
play :C
sleep 0.5
play :D
sleep 0.5
play :E

Lembre-se de colocar os dois-pontos : antes do nome de cada nota, para que ela fique cor-de-rosa (indicando que foi reconhecida). Você também pode especificar a oitava, adicionando um número após o nome de nota:

# :C3 = Dó na terceira oitava; :E4 = Mi na quarta oitava...
play :C3
sleep 0.5
play :D3
sleep 0.5
play :E4

Se quiser transformar uma nota em sustenido, adicione um s após o nome da nota, como em play :Fs3. Se você quiser transformar a nota em bemol, adicione um b como em play :Eb3.

Agora pire e divirta-se criando suas próprias melodias.


2.2 - Opções de Sintetizador: Amp (amplitude) and Pan (panorâmica)

Da mesma forma que te permite controlar qual nota tocar ou qual amostra disparar, o Sonic Pi fornece uma gama de opções para trabalhar e controlar os sons. Nós cobriremos muitas delas neste tutorial e há uma documentação extensa para cada um delas no sistema de ajuda. Entretanto, por hora vamos introduzir duas das mais úteis: amplitude e pan. Mas vamos ver primeiro o que realmente são ‘opções’.

Opções

O Sonic Pi suporta a notação de opções (ou opts, abreviando) para seus sintetizadores. Opções são controles você passa ao comando play que modificam e controlam aspectos do som que você ouve. Cada sintetizador tem seu próprio conjunto de opções para um ajuste fino do som. No entanto, existem algumas opções comuns compartilhadas por vários synths, tais como amp: e opções de envelope (cobertas em outra seção).

As opções possuem duas partes principais, seu nome (o nome do controle) e seu valor (o valor que você quer dar a este controle). Por exemplo, você pode ter uma opção chamada queijo: e quer dar a ela o valor 1.

As opções são passadas ao comando play através do uso da vírgula , seguida do nome da opção amp: (não esqueça dos dois-pontos :) e então um espaço seguido do valor da opção. Por exemplo:

play 50, queijo: 1

(Note que queijo: não é uma opção válida, nós só estamos usando como exemplo).

Você pode passar múltiplas opções, separando-as com vírgulas:

play 50, queijo: 1, feijões: 0.5

A ordem das opções não importa, então estes casos são idênticos:

play 50, feijões: 0.5, queijo: 1

Opções que não forem reconhecidas pelo sintetizador serão simplesmente ignoradas (como queijo e feijões, que são claramente nomes de opções ridículos!)

Se, por acidente, você usar a mesma opção duas vezes com valores diferentes, a segunda vence. Por exemplo, beans: aqui você terá o valor 2 em vez de 0.5:

play 50, feijões: 0.5, queijo: 3, ovos: 0.1, feijões: 2

Muitas coisas no Sonic Pi aceitam opções, então gaste um pouco de tempo aprendendo como usá-las e você estará pronto! Vamos brincar com a nossa primeira opção: amp:.

Amplitude

Amplitude é uma representação do volume de um som. Uma grande amplitude produz um som barulhento e uma baixa amplitude produz um som quieto. Da mesma forma que o Sonic Pi usa números para representar tempos e notas, ele usa números para representar amplitudes. Uma amplitude 0 é silêncio (você não ouvirá nada) enquanto uma amplitude igual a 1 é o volume normal. Você pode até estourar a amplitude para valores 2, 10, 100. Entretanto você deve notar que quando a amplitude geral, de todos os sons, fica muito alta, o Sonic Pi usa o que é chamado de compressor para espremer as amplitudes e garantir que as coisas não fiquem muito altas para seus ouvidos. Isto pode muitas vezes fazer o som ficar embolado e estranho. Então tente usar amplitudes baixas, isto é: na faixa entre 0 e 1 para evitar compressão.

Amplificando

Para mudar a amplitude de um som, você pode usar a opção amp:. Por exemplo, para tocar com a metade da amplitude passe o valor 0.5:

play 60, amp: 0.5

Para tocar com o dobro da amplitude, passe 2:

play 60, amp: 2

A opção amp: somente modifica a chamada para o playao qual está associada. Então, neste exemplo, a primeira chamada toca a nota com a metade do volume e na segunda o volume volta para o padrão (1):

play 60, amp: 0.5
sleep 0.5
play 65

Sendo assim, você pode usar valores diferentes de amp: em cada comando play:

play 50, amp: 0.1
sleep 0.25
play 55, amp: 0.2
sleep 0.25
play 57, amp: 0.4
sleep 0.25
play 62, amp: 1

Controlando o Pan

Outra opção divertida para usar é pan: que controla o som estéreo de lado-a-lado. Virar o som para a esquerda (pan left), significa que você ouvirá o som no alto-falante esquerdo, e virando para a direita (pan right), significa que você ouvirá o som no alto-falante direito. Usamos -1 para representar totalmente à esquerda, 0 para representar o centro e 1 para representar totalmente à direita, no campo estéreo. Sinta-se livre para usar qualquer valor entre -1 e 1 para controlar a posição exata do seu som.

Vamos tocar um bip no alto-falante esquerdo:

play 60, pan: -1

Agora vamos tocá-lo no alto-falante direito:

play 60, pan: 1

Finalmente, vamos tocá-lo de volta no centro (a posição padrão):

play 60, pan: 0

Agora, vá e divirta-se mudando a amplitude e a posição dos seus sons!


2.3 - Trocando Sintetizadores

Até agora nós nos divertimos bastante fazendo bips. Entretanto você provavelmente está começando a ficar cansado do barulho deste bip básico. Isso é tudo que o Sonic Pi tem a oferecer? Certamente deve haver muito mais no ‘live coding’ do que só tocar bips? Sim, há e nesta seção exploraremos a excitante variedade de sons que o Sonic Pi tem a oferecer.

Sintetizadores

O Sonic Pi tem uma variedade de instrumentos chamados synths, que é a abreviação de sintetizadores. Enquanto amostras representam sons pré-gravados, sintetizadores são capazes de gerar novos sons dependendo de como você os controla (o que exploraremos mais tarde neste tutorial). Os sintetizadores do Sonic Pi são muito poderosos e expressivos, e você se divertirá muito explorando e brincando com eles. Primeiro, vamos aprender como selecionar o sintetizador que vamos usar.

Buzzy saws (dentes de serra ruidosas) e prophets (um sintetizador da Roland)

Um som divertido é a onda dentes de serra - vamos experimentá-lo:

use_synth :saw
play 38
sleep 0.25
play 50
sleep 0.25
play 62
sleep 0.25

Vamos experimentar outro som - o prophet:

use_synth :prophet
play 38
sleep 0.25
play 50
sleep 0.25
play 62
sleep 0.25

Que tal combinarmos os dois sons. Primeiro um seguido do outro:

use_synth :saw
play 38
sleep 0.25
play 50
sleep 0.25
use_synth :prophet
play 57
sleep 0.25

Now multiple sounds at the same time (by not sleeping between successive calls to play):

use_synth :tb303
play 38
sleep 0.25
use_synth :dsaw
play 50
sleep 0.25
use_synth :prophet
play 57
sleep 0.25

Note que o comando use_synthsó afeta as próximas chamadas de play. Pense nele como um grande interruptor - novas chamadas de play irão tocar o sintetizador que tenha esteja selecionado no momento. Você pode trocar para um novo sintetizador com use_synth.

Descobrindo os Sintetizadores

Para ver quais sintetizadores o Sonic Pi tem disponíveis para você, dê uma olhada na opção Sinths, bem a esquerda no menu vertical (acima de FX). Existem mais de 20 para se escolher. Aqui estão alguns dos meus favoritos:

:prophet :dsaw :fm :tb303 :pulse

Agora brinque trocando sintetizadores durante sua música. Divirta-se combinando sintetizadores, para criar novos sons assim como usando diferentes sintetizadores em diferentes seções da sua música.


2.4 - Duração com Envelopes

Em uma seção anterior, nós vimos como podemos usar o comando sleep para controlar quando o som será disparado. Entretato, ainda não fomos capazes de controlar a duração dos nossos sons.

Para nos dar um meio simples, mas ainda assim poderoso, de controlar a duração de nossos sons, o Sonic Pi fornece a noção de um envelope de amplitude ADSR (vamos explicar o que ADSR significa mais à frente nesta seção). Um envelope de amplitude oferece dois aspectos úteis de controle:

controle sobre a duração de um som controle sobre a amplitude de um som

Duração

A duração é o tempo que o som permanece tocando. Uma duração mais longa significa que você ouve o som por mais tempo. Todos os sons do Sonic Pi possuem um envelope de amplitude controlável e a duração deste envelope é a duração do som. Portanto, controlando o envelope, você controla a duração.

Amplitude

O envelope ADSR não só controla a duração, ele também te dá controle fino sobre a amplitude do som. Todos os sons começa e terminam no silêncio e contém algumas partes não silenciosas no meio. Envelope te permitem deslizar e manter a amplitude das partes não silenciosas do som. É como dar a alguém instruções de como aumentar e diminuir o volume de um amplificador de guitarra. Por exemplo: você pode pedir para alguém para “iniciar no silêncio, aumentar o volume devagar, mantê-lo por um pouco e então voltar rapidamente para o silêncio”. O Sonic Pi te permite programar exatamente este comportamento através de envelopes.

Recapitulando, como vimos antes, uma amplitude de 0 é silêncio e uma amplitude de 1 é volume normal.

Agora, vamos ver em sequencia cada uma das partes das curvas de envelope.

Fase de Release (extinção)

A única parte do envelope que é usada por padrão é o período de Release (extinção ou relaxamento). Este é o tempo que o som do sintetizador leva para desaparecer. Todos os sintetizadores possuem um tempo de extinção de 1, que significa que, por padrão, eles duram 1 batida (que no BPM padrão de 60 é 1 segundo):

play 70

A nota será audível por 1 segundo. Vá em frente e cronometre:-) Esta é a versão curta da versão mais longa e explícita:

play 70, release: 1

Note como isso soa exatamente o mesmo (o som dura 1 segundo). Entretanto, agora é muito fácil modificar a duração modificando o valor da opção release::

play 60, release: 2

Podemos fazer com que o sintetizador soe por um período muito pequeno usando um valor bem pequeno para release::

play 60, release: 0.2

A duração deste esmaecimento do som é chamada de fase de release e por padrão é uma transição linear (isto é: uma reta). O diagrama a seguir ilustra esta transição:

envelope - fase de release

A linha vertical bem a esquerda mostra que o som começa com amplitude 0, mas aumenta até a amplitude total imediatamente (esta é a fase de ataque, que cobriremos em seguida). Uma vez na amplitude total, em seguida se move em linha reta até zero, levando o tempo especificado por release:. Fases de release mais longas produzem fade outs mais longos para o sintetizador.

Você pode, portanto, mudar a duração do seu som mudando o tempo de release. Brinque adicionando tempos de release à sua música.

Fase de Ataque

Por padrão, a fase de ataque é 0 para todos os sintetizadores, o que significa que eles vão da amplitude 0 para 1 imediatamente. Isso dá aos sintetizadores um som inicial percussivo. Entretanto, você pode querer um som com início gradual. Isso pode ser atingido com a opção attack:. Tente um início gradual (fade in) em alguns sons:

play 60, attack: 2
sleep 3
play 65, attack: 0.5

Você pode utilizar múltiplas opções ao mesmo tempo. Por exemplo, para um ataque curto e repouso longo, tente:

play 60, attack: 0.7, release: 4

Este envelopamento (ataque curto e repouso longo) é ilustrado no seguinte diagrama:

envelope de ataque e repouso

Claro que você pode inverter as coisas. Tente um ataque longo e repouso curto:

play 60, attack: 4, release: 0.7

envelope ataque longo repouso curto

Finalmente, você pode ter também os tempos, tanto de ataque quanto de release, curtos para obter sons mais curtos.

play 60, attack: 0.5, release: 0.5

envelope attack e release curtos

Fase de Sustentação

Além de especificar os tempos de ataque e release, você também pode especificar um tempo para controlar a fase de sustain (sustentação). Este é o tempo durante o qual o som é mantido em sua amplitude total, entre as fase de ataque e release.

play 60, attack: 0.3, sustain: 1, release: 1

envelope ASR

O tempo de sustentação é útil para quando você deseja aumentar a presença do som, antes de entrar na fase de release opcional. Claro, é completamente válido configurar as opções de attack: e release: com valor 0 e usar somente o sustain, ficando totalmente sem fade in ou fade out no som. Porém, fique avisado, um release igual a 0 pode produzir clicks no áudio e geralmente é preferível usar um valor pequeno, como 0.2.

Fase de Decaimento (Decay)

Para um nível extra de controle, você também pode especificar um tempo de decaimento. Esta é a fase do envelope que se encaixa entre o ataque e o sustain e especifica um tempo para que a amplitude caia de attack_level: atédecay_level: (que, a não ser que você configure explicitamente, será igual ao sustain_level:). Por padrão, a opção decay: é 0 e ambos os níveis de ataque e de sustain são 1. Desta forma, você precisa especificá-los para que o tempo de decaimento faça algum efeito:

play 60, attack: 0.1, attack_level: 1, decay: 0.2, sustain_level: 0.4, sustain: 1, release: 0.5

envelope ADSR

Nível de Decaimento

Um último truque é que apesar do valor padrão da opção decay_level: ser o mesmo valor de sustain_level:, você pode configurá-los explicitamente com valores diferentes para ter controle total sobre o envelope. Isto te permite criar envolventes como o seguinte:

play 60, attack: 0.1, attack_level: 1, decay: 0.2, decay_level: 0.3, sustain: 1, sustain_level: 0.4, release: 0.5

envelope ASR

Também é possível configurar decay_level: para ser maior que o sustain_level::

play 60, attack: 0.1, attack_level: 0.1, decay: 0.2, decay_level: 1, sustain: 0.5, sustain_level: 0.8, release: 1.5

envelope ASR

Curvas de Envelope ADSR

Para resumir, os envelopes ASDR do Sonic Pi possuem as seguintes fases:

ataque - tempo para a amplitude ir 0 até o attack_level: decaimento (decay) - tempo para a amplitude ir de attack_level: até decay_level: sustentação (sustain) - tempo para a amplitude ir de decay_level: até sustain_level: extinção (release) - tempo para a amplitude ir de sustain_level: até 0 (extinção do som)

É importante notar que a duração de um som é a somatória dos tempos de cada uma destas fases. Portanto, o seguinte som terá uma duração de 0.5 + 1 + 2 + 0.5 = 4 tempos:

play 60, attack: 0.5, attack_level: 1, decay: 1, sustain_level: 0.4, sustain: 2, release: 0.5

Agora vá e se divirta adicionando curvas de envelope aos seus sons…


3 - Amostras

Outra grande maneira de desenvolver a sua música é usando sons pré-gravados. Na grande tradição hip-hop, nós chamamos estes sons pré-gravado e samples (ou amostras, em português). Então, se você levar um microfone para a rua, vá e grave o suave som da chuva caindo e você terá criado uma amostra.

O Sonic Pi te permite fazer muitas coisas divertidas com amostras. Não só vem com 130 amostras de domínio público prontas para serem tocadas, assim como te deixa tocar e manipular suas próprias amostras. Vamos ver como…


3.1 - Disparando Amostras

Tocar bips é só o começo. Algo muito mais divertido é disparar amostras pré-programadas. Tente:

sample :ambi_lunar_land

O Sonic Pi inclui muitas amostras para você brincar. Você pode usá-las exatamente como você usa o comando play. Para tocar várias amostras e notas basta escrevê-las em sequência:

play 36
play 48
sample :ambi_lunar_land
sample :ambi_drone

Se quiser espaçá-las no tempo, use o comando sleep:

sample :ambi_lunar_land
sleep 1
play 48
sleep 0.5
play 36
sample :ambi_drone
sleep 1
play 36

Note como o Sonic Pi não espera que um som termine antes de começar a tocar o próximo. O comando sleep somente descreve a separação dos disparos de cada som. Isto te permite facilmente sobrepor sons em camadas, criando efeitos interessantes de sobreposição. Adiante, neste tutorial, nós veremos como controlar a duração dos sons com envelopes.

Descobrindo Amostras

Há duas formas de descobrir a variedade de amostras fornecidas pelo Sonic Pi. Primeiro, você pode usar este sistema de ajuda. Clique em Samples, no menu vertical bem a esquerda, escolha uma categoria e você verá uma lista de sons disponíveis.

Alternativamente, você pode usar o sistema de auto-completação. Simplesmente digite o começo de um grupo de amostras como sample :ambi_ e você verá uma lista suspensa de nomes de amostras aparecer para você selecionar. Tente os seguintes prefixos de categorias:

:ambi_ :bass_ :elec_ :perc_ :guit_ :drum_ :misc_ :bd_

Agora comece a mixar amostras nas suas composições!


3.2 - Parâmetros de Amostras: Amplitude (Amp) e Panorâmico (Pan)

Assim como fazemos com os sintetizadores, podemos controlar facilmente nossas amostras através de parâmetros. Elas suportam exatamente o mesmo mecanismo de parametrização. Vamos revisitar nossos amigos amp: e pan:.

Amplificando amostras

Você pode modificar a amplitude de amostras exatamente da mesma forma que usamos com os sintetizadores:

sample :ambi_lunar_land, amp: 0.5

Usando pan com amostras

Também podemos usar o parâmetro pan: com amostras. Por exemplo, veja como nós podemos tocar o Amem Break no canal esquerdo e no meio da execução, trocamos para o canal direito:

sample :loop_amen, pan: -1
sleep 0.877
sample :loop_amen, pan: 1

Note que 0.877 é metade da duração da amostra :loop_amen, em segundos.

Finalmente, note que se você configurar alguns valores padrão para sintetizadores com use_synth_defaults (que será discutido mais tarde), estes valores serão ignorados por sample.


3.3 - Esticando Amostras (Stretching)

Agora que podemos tocar uma variedade de sintetizadores e amostras para criar música, está na hora de aprendermos como modificar os sintetizadores e amostras para transformar a música em algo ainda mais único e interessante. Primeiro, vamos explorar a habilidade de esticar e comprimir amostras.

Now that we can play a variety of synths and samples to create some music, it’s time to learn how to modify both the synths and samples to make the music even more unique and interesting. First, let’s explore the ability to stretch and squash samples.

Representação de Amostras

Amostras são sons pré-gravados, armazenados como números que representam como mover o diafragma do alto-falante para reproduzir o som. O diafragma do alto-falante pode mover-se para dentro e para fora, e desta forma os números apenas representam a distância destes movimentos para cada momento no tempo. Para ser capaz de de reproduzir fielmente um som gravado a amostra precisa armazenar milhares de números por segundo! O Sonic Pi pega esta lista de números e os passa para o alto-falante do computador na velocidade correta para movê-lo da forma correta e reproduzir o som. Entretanto, é possível (e divertido) mudar a velocidade que os números são passados ao alto-falante para modificar o som.

Mudando a Velocidade (Rate)

Vamos brincar com um dos sons ambientes: :ambi_choir. Para tocá-lo com a velocidade (rate) padrão, você pode passar a opção rate: para sample (que é a amostra):

sample :ambi_choir, rate: 1

Isto irá tocá-la em velocidade normal (1), nada de especial ainda. Mas estamos livres para modificar este número para qualquer outra coisa. Que tal 0.5:

sample :ambi_choir, rate: 0.5

Wow! O que está acontecendo aqui? Bem, duas coisas. Primeiro, a amostra leva o dobro do tempo para ser reproduzida. Segundo, o som está uma oitava abaixo. Vamos explorar estas coisas mais detalhadamente.

Vamos esticar

Uma amostra que é divertida de esticar e comprimir é a Amem Break. Na velocidade normal, podemos imaginar inserindo-o em uma faixa de drum ‘n’ bass:

sample :loop_amen

Mas ao mudar a velocidade nós podemos trocar o gênero musical. Tente meia velocidade para um hip-hop old school:

sample :loop_amen, rate: 0.5

Se acelerarmos, entramos no território do jungle:

sample :loop_amen, rate: 1.5

Agora o truque final - vamos ver o que acontece se usarmos um valor negativo para a velocidade (rate):

sample :loop_amen, rate: -1

Wow! Toca ao contrário! Agora tente com várias amostras diferentes, em velocidades diferentes. Tente velocidades muito rápidas. Tente velocidades extremamente lentas. Veja os sons interessantes que você pode produzir.

Uma Explicação Simples do Termo Sample Rate (velocidade da amostra)

Uma forma útil de se pensar em amostras é com se fossem molas. A velocidade de execução é como espremer ou esticar a mola. Se você toca uma amostra com velocidade 2, você está comprimindo a mola até a metade de seu comprimento. Consequentemente, a amostra leva metade do tempo para tocar, já que está menor. Se você toca a amostra com metade da velocidade, você está esticando a mola até o dobro de seu tamanho. A amostra leva o dobro do tempo para ser tocada, já que está mais longa. Quanto mais você comprime (rate maior), mais curto o som fica. Quanto mais você estica (rate menor), mais longo o som fica.

Comprimir uma mola aumenta sua densidade (em número de voltas por cm) - isto é similar ao som da amostra ficar mais agudo (tom mais alto). Esticando a mola, sua densidade diminui e, no caso da amostra, o som fica mais grave (com um tom mais baixo).

A Matemática Por Trás do Sample Rate

(Esta seção é voltada para aqueles que estão interessados nos detalhes. Sinta-se a vontade para pulá-la…)

Como vimos acima, uma amostra é representada por uma longa lista de números representando onde o alto-falante deve estar através do tempo. Podemos pegar esta lista de números e usá-la para desenhar um gráfico que ficaria similar a isto:

gráfico da amostra

Você deve já ter visto gráficos com este antes. São chamados de Waveform (forma de onda) da amostra. É só um gráfico de números. Tipicamente uma onda terá 44100 pontos de dados por segundo (isto se deve ao Teorema de Amostragem de Nyquist-Shannon). Então, se a amostra durar 2 segundos, a onda será representada por 88200 números que deveremos passar ao alto-falente, à velocidade de 44100 pontos/s. Claro, nós passá-los com velocidade dobrada, que seria 88200 pontos/s. Isso então levaria somente 1 segundo para tocar. Nós podemos também tocá-la com metade da velocidade, que seriam 22050 pontos/s, levando 4 segundos pra tocar.

A duração da amostra é afetada pela velocidade (rate) que é tocada:

Dobrando a velocidade, cortamos pela metade a duração, Tocando na metade da velocidade, dobramos a duração, Tocando com 1/4 da velocidade, quadruplicamos a duração, Tocando com 1/10 da velocidade, multiplicamos a duração por 10.

Podemos representar isso através da fórmula:

nova_duração_da_amostra = (1 / rate) * duração_da_amostra 

Modificando a velocidade de execução também afeta o tom da amostra. A frequência, ou tom (pitch), de uma onda é determinada pelo quão rápido ela se move para cima e para baixo. Nossos cérebros, de alguma forma transformam movimentos rápidos do alto-falante em notas altas (agudas) e movimentos lentos do alto-falante em notas baixas (graves). Por isso que, as vezes, você pode até ver um grande alto-falante reproduzindo sons graves se movendo - na realidade, ele está se movendo bem mais lento (para dentro e para fora) do que um alto-falante reproduzindo notas altas (agudos).

Se você pegar um onda e espremê-la, ela ira se mover, para cima e para baixo, mais vezes por segundo. Isto fará o som ficar mais agudo. Acontece que dobrando a quantidade de movimentos para cima e para baixo (oscilações), dobramos a frequência. Então, tocando nossa amostra com o dobro da velocidade, dobramos a frequência que você ouve. Também, dividindo ao meio a velocidade, dividimos ao meio a frequência. Outras velocidades (rates) afetarão a frequência proporcionalmente.


3.4 - Curvas de Envelope de Amostras (ou simplesmente Envelopes de Amostras)

Também é possível modificar a duração e a amplitude de um sample usando um pacote ADSR. Entretanto, isso funciona um pouco diferente dos pacotes ADSR disponíveis em sintetizadores. Envelopes de samples permitem apenas a redução da amplitude e da duração de um sample - e nunca seu incremento. O sample irá parar de tocar quando o sample chegar ao fim ou quando o envelope se completar - O que vier primeiro. Então, se você usar um release:comprido, isso não irá extender a duração do sample.

Envelopes Amen

Vamos voltar ao nosso fiel amigo Amen Break:

sample :loop_amen

Sem opts, nós ouvimos o sample com a amplitude máxima. Se quisermos fazer um fade com mais de 1 segundo, podemos usar o parâmetro attack::

sample :loop_amen, attack: 1

Para um fade in mais curto, escolha um valor menor de attack:

sample :loop_amen, attack: 0.3

Sustain Automático

Onde o envelope ADSR tem um comportamento diferente dos sintetizadores é no valor de sustain. Em um envelope padrão de sintetizador, o sustain tem um valor 0 por padrão, a não ser que outro valor seja definido manualmente. Com samples, o sustain é definido com um valor automágico - o tempo de duração da sample. É por isso que ouvimos toda a sample quando não passamos nenhum valor. Se os valores de attack, decay, sustain e release fossem todos 0, nós não ouviríamos nem um pio. Portanto, o Sonic Pi calcula a duração da sample, deduz qualquer tempo de attack, decay e release, e usa o resultado como tempo de sustain. Se os valores de attack, decay e release somarem mais do que a duração da sample, o sustain é definido como 0.

Fade Outs

Para explorarmos isso, vamos analisar nosso Amen Break em mais detalhes. Se perguntarmos ao Sonic Pi qual é a duração do sample:

print sample_duration :loop_amen

Irá exibir o valor 1.753310657596372, que é a duração da sample em segundos. Vamos arredondar isso para 1.75 por conveniência. Agora, se definirmos o valor de release para 0.75, algo surpreendente acontece:

sample :loop_amen, release: 0.75

Primeiro, ele ira tocar o primeiro segundo do sample na amplitude máxima, e depois fará um fading out por um período de 0,75 segundos. Esse é o auto sustain em ação. Por padrão, o release sempre trabalha com o final do sample. Se o nosso sample tiver 10,75 segundos de duração, ele ira tocar os primeiros 10 segundos na amplitude máxima antes do fading out de 0,75 segundos.

Este envelopamento (ataque curto e repouso longo) é ilustrado no seguinte diagrama:

Fade In e Fade Out

Nós podemos usar ambos attack: e release: em conjunto com o auto sustain para fazer o fade-in e o fade-out na duração do sample:

sample :loop_amen, attack: 0.75, release: 0.75

As the full duration of the sample is 1.75s and our attack and release phases add up to 1.5s, the sustain is automatically set to 0.25s. This allows us to easily fade the sample in and out.

Sustentação (sustain) explícito

Nós podemos facilmente voltar para o comportamento normal do nosso sintetizador ASDR, configurando manualmente o sustain: para um valor como 0:

sample :loop_amen, sustain: 0, release: 0.75

Now, our sample only plays for 0.75 seconds in total. With the default for attack: and decay: at 0, the sample jumps straight to full amplitude, sustains there for 0s then releases back down to 0 amplitude over the release period - 0.75s.

Percussive cymbals

We can use this behaviour to good effect to turn longer sounding samples into shorter, more percussive versions. Consider the sample :drum_cymbal_open:

sample :drum_cymbal_open

You can hear the cymbal sound ringing out over a period of time. However, we can use our envelope to make it more percussive:

sample :drum_cymbal_open, attack: 0.01, sustain: 0, release: 0.1

You can then emulate hitting the cymbal and then dampening it by increasing the sustain period:

sample :drum_cymbal_open, attack: 0.01, sustain: 0.3, release: 0.1

Now go and have fun putting envelopes over the samples. Try changing the rate too for really interesting results.


3.5 - Amostras Parciais

This section will conclude our exploration of Sonic Pi’s sample player. Let’s do a quick recap. So far we’ve looked at how we can trigger samples:

sample :loop_amen

We then looked at how we can change the rate of samples such as playing them at half speed:

sample :loop_amen, rate: 0.5

Next, we looked at how we could fade a sample in (let’s do it at half speed):

sample :loop_amen, rate: 0.5, attack: 1

We also looked at how we could use the start of a sample percussively by giving sustain: an explicit value and setting both the attack and release to be short values:

sample :loop_amen, rate: 2, attack: 0.01, sustain: 0, release: 0.35

However, wouldn’t it be nice if we didn’t have to always start at the beginning of the sample? Wouldn’t it also be nice if we didn’t have to always finish at the end of the sample?

Choosing a starting point

It is possible to choose an arbitrary starting point in the sample as a value between 0 and 1 where 0 is the start of the sample, 1 is the end and 0.5 is half way through the sample. Let’s try playing only the last half of the amen break:

sample :loop_amen, start: 0.5

How about the last quarter of the sample:

sample :loop_amen, start: 0.75

Choosing a finish point

Similarly, it is possible to choose an arbitrary finish point in the sample as a value between 0 and 1. Let’s finish the amen break half way through:

sample :loop_amen, finish: 0.5

Specifying start and finish

Of course, we can combine these two to play arbitrary segments of the audio file. How about only a small section in the middle:

sample :loop_amen, start: 0.4, finish: 0.6

O que acontece se escolhermos uma posição inicial posterior a posição final?

sample :loop_amen, start: 0.6, finish: 0.4

Cool! It plays it backwards!

Combining with rate

We can combine this new ability to play arbitrary segments of audio with our friend rate:. For example, we can play a very small section of the middle of the amen break very slowly:

sample :loop_amen, start: 0.5, finish: 0.7, rate: 0.2

Combining with envelopes

Finally, we can combine all of this with our ADSR envelopes to produce interesting results:

sample :loop_amen, start: 0.5, finish: 0.8, rate: -0.2, attack: 0.3, release: 1

Now go and have a play mashing up samples with all of this fun stuff…


3.6 - External Samples

Whilst the built-in samples can get you up and started quickly, you might wish to experiment with other recorded sounds in your music. Sonic Pi totally supports this. First though, let’s have a quick discussion on the portability of your piece.

Portability

When you compose your piece purely with built-in synths and samples, the code is all you need to faithfully reproduce your music. Think about that for a moment - that’s amazing! A simple piece of text you can email around or stick in a Gist represents everything you need to reproduce your sounds. That makes it really easy to share with your friends as they just need to get hold of the code.

However, if you start using your own pre-recorded samples, you lose this portability. This is because to reproduce your music other people not only need your code, they need your samples too. This limits the ability for others to manipulate, mash-up and experiment with your work. Of course this shouldn’t stop you from using your own samples, it’s just something to consider.

Local Samples

So how do you play any arbitrary WAV, AIFF, OGG, OGA or FLAC file on your computer? All you need to do is pass the path of that file to sample:

# Raspberry Pi, Mac, Linux
sample "/Users/sam/Desktop/my-sound.wav"
# Windows
sample "C:/Users/sam/Desktop/my-sound.wav"

Sonic Pi will automatically load and play the sample. You can also pass all the standard params you’re used to passing sample:

# Raspberry Pi, Mac, Linux
sample "/Users/sam/Desktop/my-sound.wav", rate: 0.5, amp: 0.3
# Windows
sample "C:/Users/sam/Desktop/my-sound.wav", rate: 0.5, amp: 0.3

3.7 - Sample Packs

Note: this section of the tutorial covers the advanced topic of working with large directories of your own samples. This will be the case if you’ve downloaded or bought your own sample packs and wish to use them within Sonic Pi.

Feel free to skip this if you’re happy working with the built-in samples.

When working with large folders of external samples it can be cumbersome to have to type the whole path every time to trigger an individual sample.

For example, say you have the following folder on your machine:

/path/to/my/samples/

When we look inside that folder we find the following samples:

100_A#_melody1.wav 100_A#_melody2.wav 100_A#_melody3.wav 120_A#_melody4.wav 120_Bb_guit1.wav 120_Bb_piano1.wav

Typically in order to play the piano sample we can use the full path:

sample "/path/to/my/samples/120_Bb_piano1.wav"

If we want to then play the guitar sample we can use its full path too:

sample "/path/to/my/samples/120_Bb_guit.wav"

However, both of these calls to sample requires us to know the names of the samples within our directory. What if we just want to listen to each sample in turn quickly?

Indexing Sample Packs

If we want to play the first sample in a directory we just need to pass the directory’s name to sample and the index 0 as follows:

sample "/path/to/my/samples/", 0

We can even make a shortcut to our directory path using a variable:

samps = "/path/to/my/samples/"
sample samps, 0

Now, if we want to play the second sample in our directory, we just need to add 1 to our index:

samps = "/path/to/my/samples/"
sample samps, 1

Notice that we no longer need to know the names of the samples in the directory - we just need to know the directory itself (or have a shortcut to it). If we ask for an index which is larger than the number of samples, it simply wraps round just like Rings. Therefore, whatever number we use we’re guaranteed to get one of the samples in that directory.

Filtering Sample Packs

Usually indexing is enough, but sometimes we need more power to sort and organise our samples. Luckily many sample packs add useful information in the filenames. Let’s take another look at the sample file names in our directory:

100_A#_melody1.wav 100_A#_melody2.wav 100_A#_melody3.wav 120_A#_melody4.wav 120_Bb_guit1.wav 120_Bb_piano1.wav

Notice that in these filenames we have quite a bit of information. Firstly, we have the BPM of the sample (beats per minute) at the start. So, the piano sample is at 120 BPM and our first three melodies are at 100 BPM. Also, our sample names contain the key. So the guitar sample is in Bb and the melodies are in A#. This information is very useful for mixing in these samples with our other code. For example, we know we can only play the piano sample with code that’s in 120 BPM and in the key of Bb.

It turns out that we can use this particular naming convention of our sample sets in the code to help us filter out the ones we want. For example, if we’re working at 120 BPM, we can filter down to all the samples that contain the string "120" with the following:

samps = "/path/to/my/samples/"
sample samps, "120"

This will play us the first match. If we want the second match we just need to use the index:

samps = "/path/to/my/samples/"
sample samps, "120", 1

We can even use multiple filters at the same time. For example, if we want a sample whose filename contains both the substrings "120" and "A#" we can find it easily with the following code:

samps = "/path/to/my/samples/"
sample samps, "120", "A#"

Finally, we’re still free to add our usual opts to the call to sample:

samps = "/path/to/my/samples/"
sample samps, "120", "Bb", 1, lpf: 70, amp: 2

Sources

The sample filter pre-arg system understands two types of information: sources and filters. Sources are information used to create the list of potential candidates. A source can take two forms:

"/path/to/samples" - a string representing a valid path to a directory "/path/to/samples/foo.wav" - a string representing a valid path to a sample

The sample fn will first gather all sources and use them to create a large list of candidates. This list is constructed by first adding all valid paths and then by adding all the valid .flac, .aif, .aiff, .wav, .wave files contained within the directories.

For example, take a look at the following code:

samps = "/path/to/my/samples/"
samps2 = "/path/to/my/samples2/"
path = "/path/to/my/samples3/foo.wav"
sample samps, samps2, path, 0

Here, we’re combining the contents of the samples within two directories and adding a specific sample. If "/path/to/my/samples/" contained 3 samples and "/path/to/my/samples2/" contained 12, we’d have 16 potential samples to index and filter (3 + 12 + 1).

By default, only the sample files within a directory are gathered into the candidate list. Sometimes you might have a number of nested folders of samples you wish to search and filter within. You can therefore do a recursive search for all samples within all subfolders of a particular folder by adding ** to the end of the path:

samps = "/path/to/nested/samples/**"
sample samps, 0

Take care though as searching through a very large set of folders may take a long time. However, the contents of all folder sources are cached, so the delay will only happen the first time.

Finally, note that the sources must go first. If no source is given, then the set of built-in samples will be selected as the default list of candidates to work with.

Filters

Once you have a list of candidates you may use the following filtering types to further reduce the selection:

"foo" Strings will filter on substring occurrence within file name (minus directory path and extension). /fo[oO]/ Regular Expressions will filter on pattern matching of file name (minus directory path and extension). :foo - Keywords will filter candidates on whether the keyword is a direct match of the filename (minus directory path and extension). lambda{|a| ... } - Procs with one argument will be treated as a candidate filter or generator function. It will be passed the list of current candidates and must return a new list of candidates (a list of valid paths to sample files). 1 - Numbers will select the candidate with that index (wrapping round like a ring if necessary).

For example, we can filter over all the samples in a directory containing the string "foo" and play the first matching sample at half speed:

sample "/path/to/samples", "foo", rate: 0.5

See the help for sample for many detailed usage examples. Note that the ordering of the filters is honoured.

Composites

Finally, you may use lists wherever you may place a source or filter. The list will be automatically flattened and the contents will be treated as regular sources and filters. Therefore the following calls to sample are semantically equivalent:

sample "/path/to/dir", "100", "C#"
sample ["/path/to/dir", "100", "C#"]
sample "/path/to/dir", ["100", "C#"]
sample ["/path/to/dir", ["100", ["C#"]]]

Wrapping Up

This was an advanced section for people that need real power to manipulate and use sample packs. If most of this section didn’t make too much sense, don’t worry. It’s likely you don’t need any of this functionality just yet. However, you’ll know when you do need it and you can come back and re-read this when you start working with large directories of samples.


4 - Randomização (ou Aleatorização)

Obs: randomização vem do inglês random, que significa aleatório, ao acaso.

Uma boa forma de tornar sua música mais interessante é usando números aleatórios. O Sonic Pi possui uma excelente funcionalidade para adicionar aleatoriedade à sua musica, mas antes de começar temos que aprender uma verdade chocante: no Sonic Pi aleatório não é verdadeiramente aleatório. O que raio quer isto dizer? Bem, vamos ver.

Repetibilidade

Uma função muito útil para gerar números aleatórios é rrand que retorna um valor aleatório entre dois números - um min e um max (rrand é abreviatura de ranged random, aleatório no intervalo). Vamos tentar tocar uma nota aleatória:

play rrand(50, 95)

Oh, uma nota aleatória foi tocada. Foi a nota 83.7527. Uma bela nota aleatória entre 50 and 95. Opa, espera, eu previ exatamente a nota aleatória que você obteve? Algo estranho está acontecendo aqui. Tente executar o programa novamente. O quê? Escolheu 83.7527 de novo? Isso não pode ser aleatório!

A resposta é que isso não é realmente aleatório, é pseudo-aleatório. O Sonic Pi te dará números que parecem aleatórios de forma repetível. Isto é muito útil para garantir que a música que você criou em uma máquina soe idêntica nas máquinas de qualquer outra pessoa - mesmo que você use aleatoriedade na sua composição.

Claro que, em uma certa composição musical, se toda vez for ‘aleatoriamente’ escolhido o 83.7527, não seria nada interessante. Mas não acontece assim. Tente o seguinte:

loop do
  play rrand(50, 95)
  sleep 0.5
end 

Sim! Finalmente soa a aleatório. Dentro de uma dada execução, chamadas subsequentes à função random retornarão valores aleatórios. No entanto, a próxima execução irá produzir exatamente a mesma sequência de valores aleatórios e soará exatamente da mesma forma. É como se todo o código do Sonic Pi voltasse para exatamente o mesmo ponto no tempo, cada vez que o botão Run for pressionado. É o Dia da Marmota (filme: Feitiço do Tempo) da síntese musical!

Sinos assombrados

Uma linda ilustração de aleatorização em ação é o exemplo ‘haunted bells’ (sinos assombrados), que faz um loop com a amostra :perc_bell com velocidades e tempos de pausa aleatórios entre os sons de sino:

loop do
  sample :perc_bell, rate: (rrand 0.125, 1.5)
  sleep rrand(0.2, 2)
end

Corte aleatório

Outro exemplo divertido e randomização é modificar o corte de sintetizadores aleatoriamente. Um bom sintetizador para tentar isto é o emulador :tb303:

use_synth :tb303
loop do
  play 50, release: 0.1, cutoff: rrand(60, 120)
  sleep 0.125
end

Sementes de números aleatórios

E se você não gostar desta sequência particular de números aleatórios que o Sonic Pi forneceu? Bom, é totalmente possível escolher um ponto inicial diferente, escolhendo uma semente diferente via use_random_seed. A semente padrão é 0, então escolha uma semente para ter uma experiência aleatória diferente!

Considere o seguinte:

5.times do
  play rrand(50, 100)
  sleep 0.5
end

Cada vez que você executar este código, você ouvirá a mesma sequência de 5 notas. Para obter uma sequência diferente simplesmente mude a semente:

use_random_seed 40
5.times do
  play rrand(50, 100)
  sleep 0.5
end

Isto produzirá uma sequência diferente de 5 notas. Mudando a semente e ouvindo o resultado você pode encontrar algo que goste - e quando você compartilhá-lo com outros, eles ouvirão exatamente o que você ouviu também.

Veja algumas outras funções aleatórias úteis.

choose

Uma atividade muito comum é escolher um item aleatório em uma lista de itens previamente conhecidos. Por exemplo, eu poso querer tocar uma dessas nota: 60, 65 ou 72. Eu posso atingir esse objetivo com choose que me deixa escolher um item de uma lista. Primeiro, eu preciso colocar meus números em uma lista, isso é feito envolvendo eles entre colchetes e separando eles com vírgulas [60, 65, 72]. Feito isso basta passá-los para o choose:

choose([60, 65, 72])

Vamos ouvir com o que este som se parece:

loop do
  play choose([60, 65, 72])
  sleep 1
end

rrand

Nós já vimos rrand, mas vamos recapitular. Ela retorna um número aleatório entre dois valores exclusivamente (ela exclui os limites acima e abaixo). Isso significa que nunca retornará o máximo nem o mínimo - sempre retornará algo entre os dois. O número sempre será um float - número de ponto flutuante, significando que não é um inteiro e sim uma fração. Exemplos de floats retornados por rrand(20, 110):

87.5054931640625 86.05255126953125 61.77825927734375

rrand_i

Ocasionalmente você desejará um número inteiro, não um float. É ai que entra a rrand_i. Ela trabalha de forma similar a ´rrand exceto que pode retornar os valores mínimo e máximo como potenciais números aleatórios (o que significa que é inclusivo e não exclusivo, em relação aos limites). Exemplos de números retornados por rrand_i(20,100)` são:

88 86 62

rand

Isto retornará um número float aleatório, entre 0 (inclusive) e o valor máximo (exclusivo) especificado por você. Por padrão, retornará um valor entre 0 e 1. Portanto é útil para selecionar aleatoriamente valores para amp::

loop do
  play 60, amp: rand
  sleep 0.25
end

rand_i

De forma similar à relação entre rrand_i e rrand, rand_i retornará um número inteiro aleatório entre 0 e o valor máximo que você especificou.

dice (dado)

As vezes você quer simular um laçamento de dados - este é um caso especial de rrand_i onde o menor valor é sempre 1. Uma chamada para dice requer que você especifique o número de lados do dado. Um dado padrão possui 6 lados, então dice(6) agirá de forma muito similar - retornando valores 1, 2, 3, 4, 5 ou 6. No entanto, como nos jogos de RPG, você poderá encontrar um dado de 4 lados, ou um dados de 12 lados ou de 20 lados - talvez até um dado de 120 lados!

one_in

Por fim, você pode quer simular o lançamento da pontuação máxima de um dado, por exemplo em um dado padrão de 6 lados. one_in (um em…) retorna verdadeiro com a probabilidade de um sobre o número de lados do dados. Desta forma, one_in(6) retornará verdadeiro com a probabilidade de 1 em 6, ou falso nos outros caso. Valores verdadeiro e falso (true/false) são muito úteis para instruções if, que iremos cobrir na próxima seção deste tutorial.

Agora vá e bagunce seu código com alguma aleatoriedade!


5 - Estruturas de Programação

Agora que você aprendeu o básico da criação de sons com play e sample e da criação de melodias simples e ritmos com sleep entre sons, você deve estar perguntando o que mais o mundo do código pode te oferecer…

Prepare-se para uma surpresa excitante! Acontece que estruturas básicas de programação como loops, condicionais, funções e threads te dão ferramentas incrivelmente poderosas para expressar suas ideias musicais.

Vamos nos ater ao básico…


5.1 - Blocos

Uma estrutura que você verá muito no Sonic Pi são os blocos. Blocos nos permitem fazer coisas úteis com pedaços grandes de código. Por exemplo, com os parâmetros de Synth e Sample somos capazes de mudar algo em uma única linha. Entretanto, as vezes nós queremos fazer algo significativo a um número grande de linhas de código. Por exemplo, nós podemos querer fazer um loop com o bloco, para adicionar reverberação (reverb) a ele, executá-lo somente uma vez em 5, etc. Considere o seguinte código:

play 50
sleep 0.5
sample :elec_plip
sleep 0.5
play 62

Para fazermos algo com um bloco de código, precisamos dizer ao Sonic Pi onde o bloco começa e onde ele termina. Nós usamos do for iniciar e end para terminar. Por exemplo:

do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end

Entretanto, isso não está completo ainda e não irá funcionar (tente e você receberá um erro) já que não dissemos ao Sonic Pi o que queremos fazer com este bloco do/end. Dizemos isto ao Sonic Pi escrevendo comandos especiais antes do do. Veremos vários destas partes especiais de código mais tarde neste tutorial. Por enquanto é importante saber que envolvendo seu código com do e end você diz ao Sonic Pi que deseja fazer algo especial com este pedaço de código.


5.2 - Iteração e Loops

Até agora passamos um bom tempo olhando para diferentes sons que podemos fazer com blocos play e sample. Também aprendemos como disparar estes sons através do tempo usando sleep.

Como você provavelmente descobriu, você pode ter muita diversão com estes blocos básicos de construção. Entretanto, uma nova dimensão de diversão se abre quando você começa a usar o poder do código para estruturar suas músicas e composições. Nas próximas seções nós exploraremos algumas destas novas ferramentas poderosas. Primeiro, iteração e loops.

Repetição

Já escreveu algum código que você gostaria de repetir algumas vezes? Por exemplo, você pode ter algo assim:

play 50
sleep 0.5
sample :elec_blup
sleep 0.5
play 62
sleep 0.25

Mas e se nós quisermos repeti-lo 3 vezes? Bem, podemos fazer algo simples e só copiá-lo e colá-lo 3 vezes:

play 50
sleep 0.5
sample :elec_blup
sleep 0.5
play 62
sleep 0.25
play 50
sleep 0.5
sample :elec_blup
sleep 0.5
play 62
sleep 0.25
play 50
sleep 0.5
sample :elec_blup
sleep 0.5
play 62
sleep 0.25

Isso é muito código! O que acontece se você quiser mudar a amostra para :elec_plip? Você terá que encontrar todos os lugares com a amostra original :elec_blup e trocá-las. Mais importante, e se você quiser repetir o código original 50 ou 1000 vezes? Agora sim, isso será muito código e muitas linhas de código para alterar se você quiser fazer uma mudança.

Iteração

De fato, repetir código deveria ser tão simples quanto dizer faça isso 3 vezes. Bom, basicamente é. Lembra do nosso velho amigo, o bloco de código? Nós podemos usá-lo para marcar o início e o fim do código que queremos repetir três vezes. Então usamos o código especial 3.times. Então, em vez de escrever faça isso três vezes, escrevemos 3.times do - não é tão difícil. Só lembre-se de escrever end no final do código que você quer repetir:

3.times do
  play 50
  sleep 0.5
  sample :elec_blup
  sleep 0.5
  play 62
  sleep 0.25
end

Não fica muito mais bonito que copiar e colar? Podemos usar isso para criar muitas estruturas repetitivas elegantes:

4.times do
  play 50
  sleep 0.5
end
8.times do
  play 55, release: 0.2
  sleep 0.25
end
4.times do
  play 50
  sleep 0.5
end

Iterações aninhadas

Podemos colocar iterações dentro de outras iterações para criarmos padrões interessantes. Por exemplo:

4.times do
  sample :drum_heavy_kick
  2.times do
    sample :elec_blip2, rate: 2
    sleep 0.25
  end
  sample :elec_snare
  4.times do
    sample :drum_tom_mid_soft
    sleep 0.125
  end
end

Criando Loops

Se você quiser que algo se repita muitas vezes, você se encontrar usando números de repetições realmente grandes como 1000.times do. Neste caso, você provavelmente ficaria melhor que pedisse para o Sonic Pi repetir para sempre (pelo menos até pressionar o botão stop). Vamos repetir o amen break para sempre:

loop do
  sample :loop_amen
  sleep sample_duration :loop_amen
end

O mais importante a se aprender sobre loops é que eles funcionam como buracos negros para o código. Uma vez que o código entre em um loop, nunca mais sairá, até que você pressione Stop - irá só repetir e repetir o loop para sempre. Isto significa que se você tiver código após o Loop, você nunca irá ouvi-lo. Por exemplo, o chimbal após este loop nunca tocará:

loop do
  play 50
  sleep 1
end
sample :drum_cymbal_open

Agora estruture seu código com iterações e loops!


5.3 - Condicionais

A common thing you’ll likely find yourself wanting to do is to not only play a random note (see the previous section on randomness) but also make a random decision and based on the outcome run some code or some other code. For example, you might want to randomly play a drum or a cymbal. We can achieve this with an if statement.

Flipping a Coin

So, let’s flip a coin: if it’s heads, play a drum, if it’s tails, play a cymbal. Easy. We can emulate a coin flip with our one_in function (introduced in the section on randomness) specifying a probability of 1 in 2: one_in(2). We can then use the result of this to decide between two pieces of code, the code to play the drum and the code to play the cymbal:

loop do
  if one_in(2)
    sample :drum_heavy_kick
  else
    sample :drum_cymbal_closed
  end
  
  sleep 0.5
  
end

Notice that if statements have three parts:

The question to ask The first choice of code to run (if the answer to the question is yes) The second choice of code to run (if the answer to the question is no)

Typically in programming languages, the notion of yes is represented by the term true and the notion of no is represented by the term false. So we need to find a question that will give us a true or false answer which is exactly what one_in does.

Notice how the first choice is wrapped between the if and the else and the second choice is wrapped between the else and the end. Just like do/end blocks you can put multiple lines of code in either place. For example:

loop do
  if one_in(2)
    sample :drum_heavy_kick
    sleep 0.5
  else
    sample :drum_cymbal_closed
    sleep 0.25
  end
  
end

This time we’re sleeping for a different amount of time depending on which choice we make.

Simple if

Sometimes you want to optionally execute just one line of code. This is possible by placing if and then the question at the end. For example:

use_synth :dsaw
loop do
  play 50, amp: 0.3, release: 2
  play 53, amp: 0.3, release: 2 if one_in(2)
  play 57, amp: 0.3, release: 2 if one_in(3)
  play 60, amp: 0.3, release: 2 if one_in(4)
  sleep 1.5
end

This will play chords of different numbers with the chance of each note playing having a different probability.


5.4 - Threads

So you’ve made your killer bassline and a phat beat. How do you play them at the same time? One solution is to weave them together manually - play some bass, then a bit of drums, then more bass… However, the timing soon gets hard to think about, especially when you start weaving in more elements.

What if Sonic Pi could weave things for you automatically? Well, it can, and you do it with a special thing called a thread.

Infinite Loops

To keep this example simple, you’ll have to imagine that this is a phat beat and a killer bassline:

loop do
  sample :drum_heavy_kick
  sleep 1
end
loop do
  use_synth :fm
  play 40, release: 0.2
  sleep 0.5
end

As we’ve discussed previously, loops are like black holes for the program. Once you enter a loop you can never exit from it until you hit stop. How do we play both loops at the same time? We have to tell Sonic Pi that we want to start something at the same time as the rest of the code. This is where threads come to the rescue.

Threads to the Rescue

in_thread do
  loop do
    sample :drum_heavy_kick
    sleep 1
  end
end
loop do
  use_synth :fm
  play 40, release: 0.2
  sleep 0.5
end

By wrapping the first loop in an in_thread do/end block we tell Sonic Pi to run the contents of the do/end block at exactly the same time as the next statement after the do/end block (which happens to be the second loop). Try it and you’ll hear both the drums and the bassline weaved together!

Now, what if we wanted to add a synth on top. Something like:

in_thread do
  loop do
    sample :drum_heavy_kick
    sleep 1
  end
end
loop do
  use_synth :fm
  play 40, release: 0.2
  sleep 0.5
end
loop do
  use_synth :zawa
  play 52, release: 2.5, phase: 2, amp: 0.5
  sleep 2
end

Now we have the same problem as before. The first loop is played at the same time as the second loop due to the in_thread. However, the third loop is never reached. We therefore need another thread:

in_thread do
  loop do
    sample :drum_heavy_kick
    sleep 1
  end
end
in_thread do
  loop do
    use_synth :fm
    play 40, release: 0.2
    sleep 0.5
  end
end
loop do
  use_synth :zawa
  play 52, release: 2.5, phase: 2, amp: 0.5
  sleep 2
end

Runs as threads

What may surprise you is that when you press the Run button, you’re actually creating a new thread for the code to run. This is why pressing it multiple times will layer sounds over each other. As the runs themselves are threads, they will automatically weave the sounds together for you.

Scope

As you learn how to master Sonic Pi, you’ll learn that threads are the most important building blocks for your music. One of the important jobs they have is to isolate the notion of current settings from other threads. What does this mean? Well, when you switch synths using use_synth you’re actually just switching the synth in the current thread - no other thread will have their synth switched. Let’s see this in action:

play 50
sleep 1
in_thread do
  use_synth :tb303
  play 50
end
sleep 1
play 50

Notice how the middle sound was different to the others? The use_synth statement only affected the thread it was in and not the outer main run thread.

Inheritance

When you create a new thread with in_thread, the new thread will automatically inherit all of the current settings from the current thread. Let’s see that:

use_synth :tb303
play 50
sleep 1
in_thread do
  play 55
end

Notice how the second note is played with the :tb303 synth even though it was played from a separate thread? Any of the settings modified with the various use_* functions will behave in the same way.

When threads are created, they inherit all the settings from their parent but they don’t share any changes back.

Naming Threads

Finally, we can give our threads names:

in_thread(name: :bass) do
  loop do
    use_synth :prophet
    play chord(:e2, :m7).choose, release: 0.6
    sleep 0.5
  end
end
in_thread(name: :drums) do
  loop do
    sample :elec_snare
    sleep 1
  end
end

Look at the log pane when you run this code. See how the log reports the name of the thread with the message?

[Run 36, Time 4.0, Thread :bass]
 |- synth :prophet, {release: 0.6, note: 47}

Only One Thread per Name Allowed

One last thing to know about named threads is that only one thread of a given name may be running at the same time. Let’s explore this. Consider the following code:

in_thread do
  loop do
    sample :loop_amen
    sleep sample_duration :loop_amen
  end
end

Go ahead and paste that into a buffer and press the Run button. Press it again a couple of times. Listen to the cacophony of multiple amen breaks looping out of time with each other. Ok, you can press Stop now.

This is the behaviour we’ve seen again and again - if you press the Run button, sound layers on top of any existing sound. Therefore if you have a loop and press the Run button three times, you’ll have three layers of loops playing simultaneously.

However, with named threads it is different:

in_thread(name: :amen) do
  loop do
    sample :loop_amen
    sleep sample_duration :loop_amen
  end
end

Try pressing the Run button multiple times with this code. You’ll only ever hear one amen break loop. You’ll also see this in the log:

==> Skipping thread creation: thread with name :amen already exists.

Sonic Pi is telling you that a thread with the name :amen is already playing, so it’s not creating another.

This behaviour may not seem immediately useful to you now - but it will be very handy when we start to live code…


5.5 - 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!


5.6 - Variables

A useful thing to do in your code is to create names for things. Sonic Pi makes this very easy: you write the name you wish to use, an equal sign (=), then the thing you want to remember:

sample_name = :loop_amen

Here, we’ve ‘remembered’ the symbol :loop_amen in the variable sample_name. We can now use sample_name everywhere we might have used :loop_amen. For example:

sample_name = :loop_amen
sample sample_name

There are three main reasons for using variables in Sonic Pi: communicating meaning, managing repetition and capturing the results of things.

Communicating Meaning

When you write code it’s easy to just think you’re telling the computer how to do stuff - as long as the computer understands it’s OK. However, it’s important to remember that it’s not just the computer that reads the code. Other people may read it too and try to understand what’s going on. Also, you’re likely to read your own code in the future and try to understand what’s going on. Although it might seem obvious to you now - it might not be so obvious to others or even your future self!

One way to help others understand what your code is doing is to write comments (as we saw in a previous section). Another is to use meaningful variable names. Look at this code:

sleep 1.7533

Why does it use the number 1.7533? Where did this number come from? What does it mean? However, look at this code:

loop_amen_duration = 1.7533
sleep loop_amen_duration

Now, it’s much clearer what 1.7533 means: it’s the duration of the sample :loop_amen! Of course, you might say why not simply write:

sleep sample_duration(:loop_amen)

Which, of course, is a very nice way of communicating the intent of the code.

Managing Repetition

Often you see a lot of repetition in your code and when you want to change things, you have to change it in a lot of places. Take a look at this code:

sample :loop_amen
sleep sample_duration(:loop_amen)
sample :loop_amen, rate: 0.5
sleep sample_duration(:loop_amen, rate: 0.5)
sample :loop_amen
sleep sample_duration(:loop_amen)

We’re doing a lot of things with :loop_amen! What if we wanted to hear what it sounded like with another loop sample such as :loop_garzul? We’d have to find and replace all :loop_amens with :loop_garzul. That might be fine if you have lots of time - but what if you’re performing on stage? Sometimes you don’t have the luxury of time - especially if you want to keep people dancing.

What if you’d written your code like this:

sample_name = :loop_amen
sample sample_name
sleep sample_duration(sample_name)
sample sample_name, rate: 0.5
sleep sample_duration(sample_name, rate: 0.5)
sample sample_name
sleep sample_duration(sample_name)

Now, that does exactly the same as above (try it). It also gives us the ability to just change one line sample_name = :loop_amen to sample_name = :loop_garzul and we change it in many places through the magic of variables.

Capturing Results

Finally, a good motivation for using variables is to capture the results of things. For example, you may wish to do things with the duration of a sample:

sd = sample_duration(:loop_amen)

We can now use sd anywhere we need the duration of the :loop_amen sample.

Perhaps more importantly, a variable allows us to capture the result of a call to play or sample:

s = play 50, release: 8

Now we have caught and remembered s as a variable, which allows us to control the synth as it is running:

s = play 50, release: 8
sleep 2
control s, note: 62

We’ll look into controlling synths in more detail in a later section.

Warning: Variables and Threads

Whilst variables are great for giving things names and capturing the results of things, it is important to know that they should typically only be used locally within a thread. For example, don’t do this:

a = (ring 6, 5, 4, 3, 2, 1)
live_loop :sorted do
  a = a.sort
  sleep 0.5
  puts "sorted: ", a
end
live_loop :shuffled do
  a = a.shuffle
  sleep 0.5
end

In the above example we assign a ring of numbers to a variable a and then used it within two separate live_loops. In the first live loop every 0.5s we sort the ring (to (ring 1, 2, 3, 4, 5, 6)) and then print it out to the log. If you run the code, you’ll find that the printed list is not always sorted!. This may surprise you - especially that sometimes the list is printed as sorted, and sometimes it is not. This is called non-deterministic behaviour and is the result of a rather nasty problem called a race-condition. The problem is due to the fact that the second live loop is also manipulating the list (in this case shuffling it) and by the time the list is printed, sometimes it has just been sorted and sometimes it has just been shuffled. Both live loops are racing to do something different to the same variable and every time round a different loop ‘wins’.

There are two solutions to this. Firstly, don’t use the same variable in multiple live loops or threads. For example, the following code will always print a sorted list as each live loop has its own separate variable:

live_loop :shuffled do
  a = (ring 6, 5, 4, 3, 2, 1)
  a = a.shuffle
  sleep 0.5
end
live_loop :sorted do
  a = (ring 6, 5, 4, 3, 2, 1)
  a = a.sort
  sleep 0.5
  puts "sorted: ", a
end

However, sometimes we do want to share things across threads. For example, the current key, BPM, synth etc. In these cases, the solution is to use Sonic Pi’s special thread-safe state system via the fns get and set. This is discussed later on in section 10.


5.7 - Thread Synchronisation

Once you have become sufficiently advanced live coding with a number of functions and threads simultaneously, you’ve probably noticed that it’s pretty easy to make a mistake in one of the threads which kills it. That’s no big deal, because you can easily restart the thread by hitting Run. However, when you restart the thread it is now out of time with the original threads.

Inherited Time

As we discussed earlier, new threads created with in_thread inherit all of the settings from the parent thread. This includes the current time. This means that threads are always in time with each other when started simultaneously.

However, when you start a thread on its own it starts with its own time which is unlikely to be in sync with any of the other currently running threads.

Cue and Sync

Sonic Pi provides a solution to this problem with the functions cue and sync.

cue allows us to send out heartbeat messages to all other threads. By default the other threads aren’t interested and ignore these heartbeat messages. However, you can easily register interest with the sync function.

The important thing to be aware of is that sync is similar to sleep in that it stops the current thread from doing anything for a period of time. However, with sleep you specify how long you want to wait while with sync you don’t know how long you will wait - as sync waits for the next cue from another thread which may be soon or a long time away.

Let’s explore this in a little more detail:

in_thread do
  loop do
    cue :tick
    sleep 1
  end
end
in_thread do
  loop do
    sync :tick
    sample :drum_heavy_kick
  end
end

Here we have two threads - one acting like a metronome, not playing any sounds but sending out :tick heartbeat messages every beat. The second thread is synchronising on tick messages and when it receives one it inherits the time of the cue thread and continues running.

As a result, we will hear the :drum_heavy_kick sample exactly when the other thread sends the :tick message, even if the two threads didn’t start their execution at the same time:

in_thread do
  loop do
    cue :tick
    sleep 1
  end
end
sleep(0.3)
in_thread do
  loop do
    sync :tick
    sample :drum_heavy_kick
  end
end

That naughty sleep call would typically make the second thread out of phase with the first. However, as we’re using cue and sync, we automatically sync the threads bypassing any accidental timing offsets.

Cue Names

You are free to use whatever name you’d like for your cue messages - not just :tick. You just need to ensure that any other threads are syncing on the correct name - otherwise they’ll be waiting for ever (or at least until you press the Stop button).

Let’s play with a few cue names:

in_thread do
  loop do 
    cue [:foo, :bar, :baz].choose
    sleep 0.5
  end
end
in_thread do
  loop do 
    sync :foo 
    sample :elec_beep
  end
end
in_thread do
  loop do
    sync :bar
    sample :elec_flip
  end
end
in_thread do
  loop do
    sync :baz
    sample :elec_blup
  end
end

Here we have a main cue loop which is randomly sending one of the heartbeat names :foo, :bar or :baz. We then also have three loop threads syncing on each of those names independently and then playing a different sample. The net effect is that we hear a sound every 0.5 beats as each of the sync threads is randomly synced with the cue thread and plays its sample.

This of course also works if you order the threads in reverse as the sync threads will simply sit and wait for the next cue.


6 - Studio FX

One of the most rewarding and fun aspects of Sonic Pi is the ability to easily add studio effects to your sounds. For example, you may wish to add some reverb to parts of your piece, or some echo or perhaps even distort or wobble your basslines.

Sonic Pi provides a very simple yet powerful way of adding FX. It even allows you to chain them (so you can pass your sounds through distortion, then echo and then reverb) and also control each individual FX unit with opts (in a similar way to giving params to synths and samples). You can even modify the opts of the FX whilst it’s still running. So, for example, you could increase the reverb on your bass throughout the track…

Guitar Pedals

If all of this sounds a bit complicated, don’t worry. Once you play around with it a little, it will all become quite clear. Before you do though, a simple analogy is that of guitar FX pedals. There are many kinds of FX pedals you can buy. Some add reverb, others distort etc. A guitarist will plug his or her guitar into one FX pedal - i.e. distortion -, then take another cable and connect (chain) a reverb pedal. The output of the reverb pedal can then be plugged into the amplifier:

Guitar -> Distortion -> Reverb -> Amplifier

This is called FX chaining. Sonic Pi supports exactly this. Additionally, each pedal often has dials and sliders to allow you to control how much distortion, reverb, echo etc. to apply. Sonic Pi also supports this kind of control. Finally, you can imagine a guitarist playing whilst someone plays with the FX controls whilst they’re playing. Sonic Pi also supports this - but instead of needing someone else to control things for you, that’s where the computer steps in.

Let’s explore FX!


6.1 - Adding FX

In this section we’ll look at a couple of FX: reverb and echo. We’ll see how to use them, how to control their opts and how to chain them.

Sonic Pi’s FX system uses blocks. So if you haven’t read section 5.1 you might want to take a quick look and then head back.

Reverb

If we want to use reverb we write with_fx :reverb as the special code to our block like this:

with_fx :reverb do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end

Now play this code and you’ll hear it played with reverb. It sounds good, doesn’t it! Everything sounds pretty nice with reverb.

Now let’s look what happens if we have code outside the do/end block:

with_fx :reverb do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end
sleep 1
play 55

Notice how the final play 55 isn’t played with reverb. This is because it is outside the do/end block, so it isn’t captured by the reverb FX.

Similarly, if you make sounds before the do/end block, they also won’t be captured:

play 55
sleep 1
with_fx :reverb do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end
sleep 1
play 55

Echo

Existem muitos FX (efeitos especiais) para escolher. Que tal um pouco de eco?

with_fx :echo do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end

One of the powerful aspects of Sonic Pi’s FX blocks is that they may be passed opts similar to opts we’ve already seen with play and sample. For example a fun echo opt to play with is phase: which represents the duration of a given echo in beats. Let’s make the echo slower:

with_fx :echo, phase: 0.5 do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end

Let’s also make the echo faster:

with_fx :echo, phase: 0.125 do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end

Let’s make the echo take longer to fade away by setting the decay: time to 8 beats:

with_fx :echo, phase: 0.5, decay: 8 do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end

Nesting FX

One of the most powerful aspects of the FX blocks is that you can nest them. This allows you to very easily chain FX together. For example, what if you wanted to play some code with echo and then with reverb? Easy, just put one inside the other:

with_fx :reverb do
  with_fx :echo, phase: 0.5, decay: 8 do
    play 50
    sleep 0.5
    sample :elec_blup
    sleep 0.5
    play 62
  end
end

Think about the audio flowing from the inside out. The sound of all the code within the inner do/end block such as play 50 is first sent to the echo FX and the sound of the echo FX is in turn sent out to the reverb FX.

We may use very deep nestings for crazy results. However, be warned, the FX can use a lot of resources and when you nest them you’re effectively running multiple FX simultaneously. So be sparing with your use of FX especially on low powered platforms such as the Raspberry Pi.

Discovering FX

Sonic Pi ships with a large number of FX for you to play with. To find out which ones are available, click on FX in the far left of this help system and you’ll see a list of available options. Here’s a list of some of my favourites:

wobble, reverb, echo, distortion, slicer

Now go crazy and add FX everywhere for some amazing new sounds!


6.2 - FX in Practice

Although they look deceptively simple on the outside, FX are actually quite complex beasts internally. Their simplicity often entices people to overuse them in their pieces. This may be fine if you have a powerful machine, but if - like me - you use a Raspberry Pi to jam with, you need to be careful about how much work you ask it to do if you want to ensure the beats keep flowing.

Consider this code:

loop do
  with_fx :reverb do
    play 60, release: 0.1
    sleep 0.125
  end
end

In this code we’re playing note 60 with a very short release time, so it’s a short note. We also want reverb so we’ve wrapped it in a reverb block. All good so far. Except…

Let’s look at what the code does. First we have a loop which means everything inside of it is repeated forever. Next we have a with_fx block. This means we will create a new reverb FX every time we loop. This is like having a separate FX reverb pedal for every time you pluck a string on a guitar. It’s cool that you can do this, but it’s not always what you want. For example, this code will struggle to run nicely on a Raspberry Pi. All the work of creating the reverb and then waiting until it needs to be stopped and removed is all handled by with_fx for you, but this takes CPU power which may be precious.

How do we make it more similar to a traditional setup where our guitarist has just one reverb pedal which all sounds pass through? Simple:

with_fx :reverb do
  loop do
    play 60, release: 0.1
    sleep 0.125
  end
end

We put our loop inside the with_fx block. This way we only create a single reverb for all notes played in our loop. This code is a lot more efficient and would work fine on a Raspberry Pi.

A compromise is to use with_fx over an iteration within a loop:

loop do
  with_fx :reverb do
    16.times do
      play 60, release: 0.1
      sleep 0.125
    end
  end
end

This way we’ve lifted the with_fx out of the inner part of the loop and we’re now creating a new reverb every 16 notes.

This is such a common pattern that with_fx supports an opt to do exactly this but without having to write the 16.times block:

loop do
  with_fx :reverb, reps: 16 do
    play 60, release: 0.1
    sleep 0.125
  end
end

Tanto o exemplo reps: 16 quanto o exemplo 16.times do irão se comportar de forma idêntica. Em essência reps: 16 repete o código entre o bloco do/end 16 vezes. Então você pode utilizar alternadamente cada um e depois escolher aquele que te parecer mais cômodo.

Remember, there are no mistakes, just possibilities. However, some of these approaches will have a different sound and also different performance characteristics. So play around and use the approach that sounds best to you whilst also working within the performance constraints of your platform.


7 - Controlling running sounds

So far we’ve looked at how you can trigger synths and samples, and also how to change their default opts such as amplitude, pan, envelope settings and more. Each sound triggered is essentially its own sound with its own list of options set for the duration of the sound.

Wouldn’t it also be cool if you could change a sound’s opts whilst it’s still playing, just like you might bend a string of a guitar whilst it’s still vibrating?

You’re in luck - this section will show you how to do exactly this.


7.1 - Controlling Running Synths

So far we’ve only concerned ourselves with triggering new sounds and FX. However, Sonic Pi gives us the ability to manipulate and control currently running sounds. We do this by using a variable to capture a reference to a synth:

s = play 60, release: 5

Here, we have a run-local variable s which represents the synth playing note 60. Note that this is run-local - you can’t access it from other runs like functions.

Once we have s, we can start controlling it via the control function:

s = play 60, release: 5
sleep 0.5
control s, note: 65
sleep 0.5
control s, note: 67
sleep 3
control s, note: 72

The thing to notice is that we’re not triggering 4 different synths here - we’re just triggering one synth and then change the pitch 3 times afterwards, while it’s playing.

We can pass any of the standard opts to control, so you can control things like amp:, cutoff: or pan:.

Non-controllable Options

Some of the opts can’t be controlled once the synth has started. This is the case for all the ADSR envelope parameters. You can find out which opts are controllable by looking at their documentation in the help system. If the documentation says Can not be changed once set, you know it’s not possible to control the opt after the synth has started.


7.2 - Controlling FX

It is also possible to control FX, although this is achieved in a slightly different way:

with_fx :reverb do |r|
  play 50
  sleep 0.5
  control r, mix: 0.7
  play 55
  sleep 1
  control r, mix: 0.9
  sleep 1
  play 62
end

Instead of using a variable, we use the goalpost parameters of the do/end block. Inside the | bars, we need to specify a unique name for our running FX which we then reference from the containing do/end block. This behaviour is identical to using parameterised functions.

Now go and control some synths and FX!


7.3 - Sliding Opts

Whilst exploring the synth and FX opts, you might have noticed that there are a number of opts ending with _slide. You might have even tried calling them and seeing no effect. This is because they’re not normal parameters, they’re special opts that only work when you control synths as introduced in the previous section.

Consider the following example:

s = play 60, release: 5
sleep 0.5
control s, note: 65
sleep 0.5
control s, note: 67
sleep 3
control s, note: 72

Here, you can hear the synth pitch changing immediately on each control call. However, we might want the pitch to slide between changes. As we’re controlling the note: parameter, to add slide, we need to set the note_slide parameter of the synth:

s = play 60, release: 5, note_slide: 1
sleep 0.5
control s, note: 65
sleep 0.5
control s, note: 67
sleep 3
control s, note: 72

Now we hear the notes being bent between the control calls. It sounds nice, doesn’t it? You can speed up the slide by using a shorter time such as note_slide: 0.2 or slow it down by using a longer slide time.

Every parameter that can be controlled has a corresponding _slide parameter for you to play with.

Sliding is sticky

Once you’ve set a _slide parameter on a running synth, it will be remembered and used every time you slide the corresponding parameter. To stop sliding you must set the _slide value to 0 before the next control call.

Sliding FX Opts

It is also possible to slide FX opts:

with_fx :wobble, phase: 1, phase_slide: 5 do |e|
  use_synth :dsaw
  play 50, release: 5
  control e, phase: 0.025
end

Now have fun sliding things around for smooth transitions and flowing control…


8 - Estruturas de dados

Uma ferramenta muito útil na caixa de ferramentas de um programador é uma estrutura de dados.

As Vezes você pode querer representar e usar mais de uma coisa. Por exemplo, você pode achar útil ter uma série de notas para tocar uma após a outra. Linguagens de programação possuem estruturas de dados que te possibilitam fazer exatamente isso.

Existem muitas estruturas de dados excitantes e exóticas disponíveis para programadores - e as pessoas estão sempre inventando novas. No entanto, por enquanto nós só precisamos considerar uma estrutura de dados bem simples - a lista.

Vamos olhá-la mais de perto. Vamos explicar sua forma básica e também como as listas podem ser usadas para representar escalas e acordes.


8.1 - Listas

Nesta seção nós vamos ver uma estrutura de dados que é muito útil - a Lista. Nós a encontramos antes, muito rapidamente, na seção sobre aleatorização quando nós, aleatoriamente, escolhemos notas para serem todas de uma lista:

play choose([50, 55, 62])

Nesta seção nós vamos explorar o uso de listas para representação de acordes e escalas. Primeiro vamos recapitular como nós podemos tocar um acorde. Lembre-se que se não usarmos o sleep, todos os sons acontecem ao mesmo tempo:

play 52
play 55
play 59

Vamos ver outras formas de representar este código.

Tocando uma lista

Uma das opções é colocar todas as notas em uma lista: [52, 55, 59]. A nossa conhecida função play é suficientemente inteligente para saber como tocar uma lista de notas. Tente:

play [52, 55, 59]

Ooh, isso já fica bem melhor para ler. Tocar uma lista de notas não te impede de usar qualquer outro dos parâmetros normais:

play [52, 55, 59], amp: 0.3

Claro, que você também pode usar os nomes tradicionais das notas em vez do seu número MIDI:

play [:E3, :G3, :B3]

Agora aqueles de vocês que tiveram sorte de ter estudado teoria musical podem reconhecer este acorde como Mi Menor tocado na terceira oitava.

Acessando uma lista

Uma outra característica muito útil de uma lista é a habilidade de receber informações dela. Isso pode parecer estranho, mas não é mais complicado do que alguém pedindo para você abrir um livro na página 23. Com uma lista, você pode dizer, qual é o elemento no índice 23? A única coisa estranha é que na programação os índices geralmente começam em 0 e não em 1.

Nos índices de listas nós não contamos 1, 2, 3… Nós contamos 0, 1, 2…

Vamos ver isto com um pouco mais de detalhe. Dê uma olhada nesta lista:

[52, 55, 59]

Não há nada especialmente assustador nisso. Agora, qual é o segundo elemento da lista? Sim, claro, é o 55. Isso foi fácil. Vamos ver se conseguimos fazer o computador responder isso por nós, também:

puts [52, 55, 59][1]

OK, isso pode parecer um pouco estranho se você nunca tinha visto nada com isso antes. Acredite em mim, não e nada difícil. Existem 3 partes na linha acima: a palavra puts, a nossa lista 52, 55, 59 e o nosso índice[1]. Inicialmente estamos dizendo puts porque queremos que o Sonic Pi imprima a resposta para nós, no registro. Em seguida, nós estamos fornecendo nossa lista, e finalmente nosso índice está pedindo o segundo elemento. A contagem começa em 0, o índice do segundo elemento é 1 (0=primeiro; 1=segundo; 2=terceiro; …). Devemos colocar nosso índice dentro de colchetes. Veja:

# índices:  0   1   2
           [52, 55, 59]

Tente executar o código puts [52, 55, 59][1] e você verá 55 aparecer no log. Mude o índice 1 para outros índices, tente com listas maiores e pense em como você pode usar listas na sua próxima apresentação. Por exemplo, que estruturas musicais podem ser representadas como uma série de números…


8.2 - Acordes

O Sonic Pi possui suporte para nomes de acordes que devolverão listas. Tente você mesmo:

play chord(:E3, :minor)

Agora estamos chegando em algum lugar. Isso parece muito mais bonito que listas brutas (e são mais fáceis de ler para outras pessoas). Então quais outros acordes o Sonic Pi suporta? Bem, muitos. Tente alguns destes:

chord(:E3, :m7) chord(:E3, :minor) chord(:E3, :dim7) chord(:E3, :dom7)

Arpejos

Podemos facilmente transformar acordes em arpejos com a função play_pattern:

play_pattern chord(:E3, :m7)

Ok, isto não é muito divertido - tocou muito devagar. play_pattern irá tocar cada nota na lista separada por uma chamada a sleep 1 entre cada chamada a play. Podemos usar outra função play_pattern_timed para especificar o nosso próprio tempo e acelerar as coisas:

play_pattern_timed chord(:E3, :m7), 0.25

Podemos até passar uma lista de tempos que serão tratados como um circulo de tempos:

play_pattern_timed chord(:E3, :m13), [0.25, 0.5]

Isto é equivalente a:

play 52
sleep 0.25
play 55
sleep 0.5
play 59
sleep 0.25
play 62
sleep 0.5
play 66
sleep 0.25
play 69
sleep 0.5
play 73

Qual você prefere escrever?


8.3 - Escalas

O Sonic Pi suporta uma grande variedade de escalas. Que tal tocar uma escala de Dó Maior na 3 oitava?

play_pattern_timed scale(:c3, :major), 0.125, release: 0.1

Podemos até pedir mais oitavas:

play_pattern_timed scale(:c3, :major, num_octaves: 3), 0.125, release: 0.1

Que tal todas as notas de uma escala pentatônica?

play_pattern_timed scale(:c3, :major_pentatonic, num_octaves: 3), 0.125, release: 0.1

Notas aleatórias

Acordes e escalas são ótimas formas para restringir as escolhas aleatórias para algo significativo. Divirta-se com este exemplo que pega notas aleatórias do acorde Mi3 menor:

use_synth :tb303
loop do
  play choose(chord(:E3, :minor)), release: 0.3, cutoff: rrand(60, 120)
  sleep 0.25
end

Tente mudar para nomes de acordes diferentes e amplitudes de corte.

Descobrindo Acordes e Escalas

Para saber quais escalas e acordes são suportados pelo Sonic Pi simplesmente clique no botão Lang, na esquerda extrema deste tutorial e escolha acorde ou escala na lista de API. Na informação do painel principal, role para baixo até ver uma longa lista de acordes ou escalas (dependendo de qual página você estiver vendo).

Divirta-se e lembre-se, não existem erros, apenas oportunidades.


8.4 - Rings

An interesting spin on standard lists are rings. If you know some programming, you might have come across ring buffers or ring arrays. Here, we’ll just go for ring - it’s short and simple.

In the previous section on lists we saw how we could fetch elements out of them by using the indexing mechanism:

puts [52, 55, 59][1]

Now, what happens if you want index 100? Well, there’s clearly no element at index 100 as the list has only three elements in it. So Sonic Pi will return you nil which means nothing.

However, consider you have a counter such as the current beat which continually increases. Let’s create our counter and our list:

counter = 0
notes = [52, 55, 59]

We can now use our counter to access a note in our list:

puts notes[counter]

Great, we got 52. Now, let’s increment our counter and get another note:

counter = (inc counter)
puts notes[counter]

Super, we now get 55 and if we do it again we get 59. However, if we do it again, we’ll run out of numbers in our list and get nil. What if we wanted to just loop back round and start at the beginning of the list again? This is what rings are for.

Creating Rings

We can create rings one of two ways. Either we use the ring function with the elements of the ring as parameters:

(ring 52, 55, 59)

Or we can take a normal list and convert it to a ring by sending it the .ring message:

[52, 55, 59].ring

Indexing Rings

Once we have a ring, you can use it in exactly the same way you would use a normal list with the exception that you can use indexes that are negative or larger than the size of the ring and they’ll wrap round to always point at one of the ring’s elements:

(ring 52, 55, 59)[0] #=> 52
(ring 52, 55, 59)[1] #=> 55
(ring 52, 55, 59)[2] #=> 59
(ring 52, 55, 59)[3] #=> 52
(ring 52, 55, 59)[-1] #=> 59

Using Rings

Let’s say we’re using a variable to represent the current beat number. We can use this as an index into our ring to fetch notes to play, or release times or anything useful we’ve stored in our ring regardless of the beat number we’re currently on.

Scales and Chords are Rings

A useful thing to know is that the lists returned by scale and chord are also rings and allow you to access them with arbitrary indexes.

Ring Constructors

In addition to ring there are a number of other functions which will construct a ring for us.

range invites you specify a starting point, end point and step size. bools allows you to use 1s and 0s to succinctly represent booleans. knit allows you to knit a sequence of repeated values. spread creates a ring of bools with a Euclidean distribution.

Take a look at their respective documentation for more information.


8.5 - Ring Chains

In addition to the constructors such as range and spread another way of creating new rings is to manipulate existing rings.

Chain Commands

To explore this, take a simple ring:

(ring 10, 20, 30, 40, 50)

What if we wanted it backwards? Well we’d use the chain command .reverse to take the ring and turn it around:

(ring 10, 20, 30, 40, 50).reverse  #=> (ring 50, 40, 30, 20, 10)

Now, what if we wanted the first three values from the ring?

(ring 10, 20, 30, 40, 50).take(3)  #=> (ring 10, 20, 30)

Finally, what if we wanted to shuffle the ring?

(ring 10, 20, 30, 40, 50).shuffle  #=> (ring 40, 30, 10, 50, 20)

Multiple Chains

This is already a powerful way of creating new rings. However, the real power comes when you chain a few of these commands together.

How about shuffling the ring, dropping 1 element and then taking the next 3?

Let’s take this in stages:

(ring 10, 20, 30, 40, 50) - our initial ring (ring 10, 20, 30, 40, 50).shuffle - shuffles - (ring 40, 30, 10, 50, 20) (ring 10, 20, 30, 40, 50).shuffle.drop(1) - drop 1 - (ring 30, 10, 50, 20) (ring 10, 20, 30, 40, 50).shuffle.drop(1).take(3) - take 3 - (ring 30, 10, 50)

Can you see how we can just create a long chain of these methods by just sticking them together. We can combine these in any order we want creating an extremely rich and powerful way of generating new rings from existing ones.

Immutability

These rings have a powerful and important property. They are immutable which means that they can not change. This means that the chaining methods described in this section do not change rings rather they create new rings. This means you’re free to share rings across threads and start chaining them within a thread knowing you won’t be affecting any other thread using the same ring.

Available Chain Methods

Here’s a list of the available chain methods for you to play with:

.reverse - returns a reversed version of the ring .sort - creates a sorted version of the ring .shuffle - creates a shuffled version of the ring choose .pick(3) - returns a ring with the results of calling .choose 3 times .take(5) - returns a new ring containing only the first 5 elements .drop(3) - returns a new ring with everything but the first 3 elements .butlast - returns a new ring with the last element missing .drop_last(3) - returns a new ring with the last 3 elements missing .take_last(6)- returns a new ring with only the last 6 elements .stretch(2) - repeats each element in the ring twice .repeat(3) - repeats the entire ring 3 times .mirror - adds the ring to a reversed version of itself .reflect - same as mirror but doesn’t duplicate middle value .scale(2) - returns a new ring with all elements multiplied by 2 (assumes ring contains numbers only)

Of course, those chain methods that take numbers can take other numbers too! So feel free to call .drop(5) instead of .drop(3) if you want to drop the first 5 elements.


9 - Programação ao vivo

Um dos aspectos mais empolgantes do Sonic Pi é que ele te permite escrever e modificar código ao vivo para criar música, da mesma forma que você pode se apresentar ao vivo com uma guitarra. Uma vantagem desta abordagem é te dar mais feedback durante a composição (deixar um loop simples executando e continuar ajustando-o até que ele soe perfeitamente). Entretanto, a maior vantagem é que você pode levar o Sonic Pi para o palco e tocar com ele.

Nesta seção cobriremos os fundamentos para tornar suas composições de código estático em performances dinâmicas.

Segurem-se em seus assentos…


9.1 - Programação ao vivo

Agora nós já aprendemos o suficiente para realmente começar a ter alguma diversão. Nesta seção nós iremos buscar itens de todas as seções anteriores e te mostrar como você pode começar a criar suas composições musicais ao vivo e transformá-las em uma performance. Para isso precisamos de 3 ingredientes principais:

Habilidade para escrever código que gere som - CHECK! Habilidade para escrever funções - CHECK! Habilidade para usar threads (nomeados) - CHECK!

Tudo bem, vamos começar. Vamos programar ao vivo nossos primeiros sons. Primeiro precisamos de uma função contendo o código que desejamos tocar. Vamos começar de forma simples. Nós também queremos chamar esta função em Loop, dentro de uma thread:

define :my_loop do #função
  play 50
  sleep 1
end
in_thread(name: :looper) do #thread
  loop do
    my_loop
  end
end

Se isto está parecendo muito complicado para você, volte e releia as seções sobre funções e threads. Não será complicado se você já tiver estudado estes itens.

O que temos aqui é uma definição de função que apenas toca a nota 50 e descansa por um beat. Em seguida, definimos uma thread chamada :looper que, em loop, chama my_looprepetidamente.

Se você executar este código, você ouvirá a nota 50 repetindo e repetindo e repetindo…

Modificando

Agora é que a diversão começa. Com o código ainda executando, modifique o 50 para um outro número, 55 por exemplo, e então pressione o botão Run outra vez. WOW! Mudou! Ao vivo!

Isso não adicionou uma nova camada porque estamos usando threads nomeadas, que permitem apenas uma thread com cada nome. Também, o som mudou porque nós redefinimos a função. Nós demos uma nova definição para :my_loop. Quando a thread :looper se repetiu ela simplesmente chamou a nova definição.

Tente modificar novamente, mude a nota, mude o tempo de descanso. Que tal adicionar um comando use_synth? Por exemplo, mude-o para:

define :my_loop do
  use_synth :tb303  # novo sintetizador
  play 50, release: 0.3
  sleep 0.25
end

Agora isso soa bem interessante, mas nós podemos apimentar um pouco mais as coisas. Em vez de tocar sempre a mesma nota, tente tocar um acorde:

define :my_loop do
  use_synth :tb303
  play chord(:e3, :minor), release: 0.3 # Mi menor
  sleep 0.5
end

Que tal tocar notas aleatórias do acorde:

define :my_loop do
  use_synth :tb303
  play choose(chord(:e3, :minor)), release: 0.3
  sleep 0.25
end
#

Ou usar um valor aleatório para cutoff:

define :my_loop do
  use_synth :tb303
  play choose(chord(:e3, :minor)), release: 0.2, cutoff: rrand(60, 130)
  sleep 0.25
end
#

Finalmente, adicione alguma bateria:

define :my_loop do
  use_synth :tb303
  sample :drum_bass_hard, rate: rrand(0.5, 2)
  play choose(chord(:e3, :minor)), release: 0.2, cutoff: rrand(60, 130)
  sleep 0.25
end
#

Agora as coisas estão ficando excitantes!

Entretanto, antes de pular e começar a programar ao vivo com funções e threads, pare o que você está fazendo e leia a próxima seção sobre live_loop que irá mudar a forma como programa no Sonic Pi para sempre…


9.2 - Live Loops (loops ao vivo)

Ok, esta seção do tutorial é joia verdadeira. Se você tiver que ler somente uma seção, que seja esta. Se você leu a seção anterior sobre os Fundamentos do Live Coding, live_loop é uma forma simples de fazer exatamente isso, mas sem ter que escrever tanto.

Se você não leu a seção anterior, live_loopé a melhor maneira de improvisar com o Sonic Pi.

Vamos tocar. Escreva o seguinte em um buffer novo:

live_loop :foo do
  play 60
  sleep 1
end
#

Agora pressione o botão Run. Você irá ouvir um beep básico a cada batida. Nada divertido isso. Entretanto, não pressione Stop ainda. Mude o 60 para 65 e clique no Run outra vez.

WOW! Mudou automaticamente sem perder a batida. Isto é Live coding.

Por que não mudar para algo mais parecido com um baixo? Somente atualize seu código enquanto ele está tocando:

live_loop :foo do
  use_synth :prophet
  play :e1, release: 8
  sleep 8
end

Então pressione Run.

Vamos fazer o ‘cutoff’ variar aleatoriamente:

live_loop :foo do
  use_synth :prophet
  play :e1, release: 8, cutoff: rrand(70, 130)
  sleep 8
end

Pressione o botão Run outra vez.

Adicione alguma bateria:

live_loop :foo do
  sample :loop_garzul
  use_synth :prophet
  play :e1, release: 8, cutoff: rrand(70, 130)
  sleep 8
end
#

Mude a nota de e1 para c1:

live_loop :foo do
  sample :loop_garzul
  use_synth :prophet
  play :c1, release: 8, cutoff: rrand(70, 130)
  sleep 8
end
#

Agora pare de me ouvir e vá tocar você mesmo! Divirta-se!


9.3 - Múltiplos Live Loops

Considere o seguinte live loop:

live_loop :foo do
  play 50
  sleep 1
end

Você pode estar se perguntando por que precisa do nome :foo. Este nome é importante porque significa que este live loop é diferente de todos os outros live loops.

Nunca pode haver dois live loops sendo executados com o mesmo nome.

Isso quer dizer que se quisermos múltiplos live loops concorrentes, teremos que dar nomes diferentes a eles:

live_loop :foo do
  use_synth :prophet
  play :c1, release: 8, cutoff: rrand(70, 130)
  sleep 8
end
live_loop :bar do
  sample :bd_haus
  sleep 0.5
end

Agora você pode atualizar e modificar cada live loop independentemente e tudo simplesmente funciona.

Sincronizando live loops

Uma coisa que você já deve ter notado é que live loops funcionam automaticamente com o mecanismo de cue do thread que exploramos anteriormente.

Considere este código mal sincronizado:

live_loop :foo do
  play :e4, release: 0.5
  sleep 0.4
end
live_loop :bar do
  sample :bd_haus
  sleep 1
end

Vejamos se conseguimos corrigir o tempo e sincronismo sem pará-lo. Primeiro vamos consertar o loop :foo para fazê-lo soar em um fator múltiplo do tempo do outro loop - algo como 0.5 servirá:

live_loop :foo do
  play :e4, release: 0.5
  sleep 0.5
end
live_loop :bar do
  sample :bd_haus
  sleep 1
end

Ainda não terminamos - você irá notar que as batidas não se alinham corretamente. Isso acontece porque os loops estão fora de fase. Vamos corrigir isso sincronizando um com o outro:

live_loop :foo do
  play :e4, release: 0.5
  sleep 0.5
end
live_loop :bar do
  sync :foo
  sample :bd_haus
  sleep 1
end

Wow, agora tudo está perfeitamente no tempo - tudo sem parar.

Agora vá em frente e programe ao vivo com live loops!


9.4 - Ticking

Something you’ll likely find yourself doing a lot when live coding is looping through rings. You’ll be putting notes into rings for melodies, sleeps for rhythms, chord progressions, timbral variations, etc. etc.

Ticking Rings

Sonic Pi provides a very handy tool for working with rings within live_loops. It’s called the tick system. In the section about the rings we were talking about the counter that is constantly increasing, like a current beat number. Tick just implements this idea. It provides you with the ability to tick through rings. Let’s look at an example:

counter = 0
live_loop :arp do
  play (scale :e3, :minor_pentatonic)[counter], release: 0.1
  counter += 1
  sleep 0.125
end

This is equivalent to:

live_loop :arp do
  play (scale :e3, :minor_pentatonic).tick, release: 0.1
  sleep 0.125
end

Here, we’re just grabbing the scale E3 minor pentatonic and ticking through each element. This is done by adding .tick to the end of the scale declaration. This tick is local to the live loop, so each live loop can have its own independent tick:

live_loop :arp do
  play (scale :e3, :minor_pentatonic).tick, release: 0.1
  sleep 0.125
end
live_loop :arp2 do
  use_synth :dsaw
  play (scale :e2, :minor_pentatonic, num_octaves: 3).tick, release: 0.25
  sleep 0.25
end

Tick

You can also call tick as a standard fn and use the value as an index:

live_loop :arp do
  idx = tick
  play (scale :e3, :minor_pentatonic)[idx], release: 0.1
  sleep 0.125
end

However, it is much nicer to call .tick at the end. The tick fn is for when you want to do fancy things with the tick value and for when you want to use ticks for other things than indexing into rings.

Look

The magical thing about tick is that not only does it return a new index (or the value of the ring at that index) it also makes sure that next time you call tick, it’s the next value. Take a look at the examples in the docs for tick for many ways of working with this. However, for now, it’s important to point out that sometimes you’ll want to just look at the current tick value and not increase it. This is available via the look fn. You can call look as a standard fn or by adding .look to the end of a ring.

Naming Ticks

Finally, sometimes you’ll need more than one tick per live loop. This is achieved by giving your tick a name:

live_loop :arp do
  play (scale :e3, :minor_pentatonic).tick(:foo), release: 0.1
  sleep (ring 0.125, 0.25).tick(:bar)
end

Here we’re using two ticks one for the note to play and another for the sleep time. As they’re both in the same live loop, to keep them separate we need to give them unique names. This is exactly the same kind of thing as naming live_loops - we just pass a symbol prefixed with a :. In the example above we called one tick :foo and the other :bar. If we want to look at these we also need to pass the name of the tick to look.

Don’t make it too complicated

Most of the power in the tick system isn’t useful when you get started. Don’t try and learn everything in this section. Just focus on ticking through a single ring. That’ll give you most of the joy and simplicity of ticking through rings in your live_loops.

Take a look at the documentation for tick where there are many useful examples and happy ticking!


10 - Time State

Often it is useful to have information that is shared across multiple threads or live loops. For example, you might want to share a notion of the current key, BPM or even more abstract concepts such as the current ‘complexity’ (which you’d potentially interpret in different ways across different threads). We also don’t want to lose any of our existing determinism guarantees when doing this. In other words, we’d still like to be able to share code with others and know exactly what they’ll hear when they run it. At the end of Section 5.6 of this tutorial we briefly discussed why we should not use variables to share information across threads due to a loss of determinism (in turn due to race conditions).

Sonic Pi’s solution to the problem of easily working with global variables in a deterministic way is through a novel system it calls Time State. This might sound complex and difficult (in fact, in the UK, programming with multiple threads and shared memory is typically a university level subject). However, as you’ll see, just like playing your first note, Sonic Pi makes it incredibly simple to share state across threads whilst still keeping your programs thread-safe and deterministic..

Meet get and set


10.1 - Set e Get

Sonic Pi has a global memory store called Time State. The two main things you do with it are to set information and get information. Let’s dive deeper…

Set

To store information into the Time State we need two things:

the information we want to store, a unique name (key) for the information.

For example, we might want to store the number 3000 with the key :intensity. This is possible using the set function:

set :intensity, 3000

We can use any name for our key. If information has already been stored with that key, our new set will override it:

set :intensity, 1000
set :intensity, 3000

In the above example, as we stored both numbers under the same key, the last call to set ‘wins’, so the number associated with :intensity will be 3000 as the first call to set is effectively overridden.

Get

To fetch information from the Time State we just need the key we used to set it, which in our case is :intensity. We then just need to call get[:intensity] which we can see by printing out the result to the log:

print get[:intensity] #=> prints 3000

Notice that calls to get can return information that was set in a previous run. Once a piece of information has been set it is available until either the information is overridden (just like we clobbered the :intensity value of 1000 to 3000 above) or Sonic Pi is closed.

Multiple Threads

The main benefit of the Time State system is that it can be safely used across threads or live loops. For example, you could have one live loop setting information and another one getting it:

live_loop :setter do
  set :foo, rrand(70, 130)
  sleep 1
end
live_loop :getter do
  puts get[:foo]
  sleep 0.5
end

The nice thing about using get and set across threads like this is that it will always produce the same result every time you hit run. Go on, try it. See if you get the following in your log:

{run: 0, time: 0.0}
 └─ 125.72265625
{run: 0, time: 0.5}
 └─ 125.72265625
{run: 0, time: 1.0}
 └─ 76.26220703125
{run: 0, time: 1.5}
 └─ 76.26220703125
{run: 0, time: 2.0}
 └─ 114.93408203125
{run: 0, time: 2.5}
 └─ 114.93408203125
{run: 0, time: 3.0}
 └─ 75.6048583984375
{run: 0, time: 3.5}
 └─ 75.6048583984375

Try running it a few times - see, it’s the same every time. This is what we call deterministic behaviour and it’s really very important when we want to share our music as code and know that the person playing the code is hearing exactly what we wanted them to hear (just like playing an MP3 or internet stream sounds the same for all listeners).

A Simple Deterministic State System

Back in Section 5.6 we discussed why using variables across threads can lead to random behaviour. This stops us from being able to reliably reproduce code such as this:

## Um exemplo de comportamento não-determinístico
## (devido a race conditions causadas por múltiplos
## loops manipulando a mesma variárvel ao mesmo tempo).
##  
## Se você executar este código você notará
## que a lista que é impressa
## nem sempre sai ordenada!
a = (ring 6, 5, 4, 3, 2, 1)
live_loop :shuffled do
  a = a.shuffle
  sleep 0.5
end
live_loop :sorted do
  a = a.sort
  sleep 0.5
  puts "sorted: ", a
end

Let’s take a look at how this might look using get and set:

## Um exemplo de comportamento determinístico
## (apesar do acesso simultâneo ao estado compartilhado)
## usando o novo sistema Time State do Sonic Pi.
##
## Quando este código é executado, a lista
## sempre é impressa de forma ordenada!
set :a, (ring 6, 5, 4, 3, 2, 1)
live_loop :shuffled do
  set :a, get[:a].shuffle
  sleep 0.5
end
live_loop :sorted do
  set :a, get[:a].sort
  sleep 0.5
  puts "sorted: ", get[:a]
end

Notice how this code is pretty much identical to the version using a variable before it. However when you run the code, it behaves as you would expect with any typical Sonic Pi code - it does the same thing every time in this case thanks to the Time State system.

Therefore, when sharing information across live loops and threads, use get and set instead of variables for deterministic, reproducible behaviour.


10.2 - Sincronização (Sync)

A seção 5.7 introduziu as funções cue e sync para lidar com problemas de sincronização de threads. O que não foi explicado é que é o sistema de Time State que fornece esta funcionalidade. É de tal forma que set is na realidade uma variação de cuee foi construído de forma que que possa trabalhar perfeitamente com o Time State - qualquer informaçãoque nós planejamos armazenar no Time State nós podemos sincronizar. Em outras palavras - nós sincronizamos synceventos que ainda serão inseridos no Time State.

Aguardando por eventos

Vamos dar uma olhada em como usar sync para esperar por novos eventos que serão inseridos ao Time State:

in_thread do
  sync :foo
  sample :ambi_lunar_land
end
sleep 2
set :foo, 1

Neste exemplo nós primeiro criamos uma thread que espera um evento :foo ser adicionado ao Time State. Após a declaração desta thread, nós esperamos por 2 batidas e então setamos (set) :foo para ser 1. Isso libera a sincronização (sync) que então segue para a próxima linha que dispara a amostra :ambi_lunar_land.

Note que sync sempre espera por eventos futuros e isso irá bloquear a a thread atual esperando por um novo evento. Também, irá herdar o tempo lógico da thread que chamou a sincronização através de set ou cue então pode também ser usado para sincronizar tempo.

Passando valores para o Futuro

No exemplo acima nós configuramos :foo igual a 1 e não fizemos nada com isso. Nós podemos, na verdade, recuperar este valor na thread chamando sync:

in_thread do
  amp = sync :foo 
  sample :ambi_lunar_land, amp: amp
end
sleep 2
set :foo, 0.5

Note que valores que são passados através de set e cue devem ser seguros - i.e. Rings imutáveis, números, símbolos ou strings congeladas. O Sonic Pi irá gerar um erro se o valor que você está tentando armazenar no Time State não for válido.


10.3 - Correspondência de Padrões

Para passar e recuperar informações para o Time State, é possível usar chaves mais complexas que símbolos básicos como :foo e :bar. Você também pode usar strings no formato URL, chamadas de Paths (caminhos) como "/foo/bar/baz". Depois que começãmos a usar paths, nós podemos começar a aproveitar o sofisticado sistema de correspondência de Padrões do Sonic Pi para usar get e sync com paths ‘similares’ em vez de ‘idênticos’. Vamos dar uma olhada.

Correspondência com qualquer parte do caminho (path)

Vamos assumir que nós queiramos esperar pelo próximo evento que tem três segmentos no path:

sync "/*/*/*"

Isto irá corresponder a qualquer evento Time State com exatamente três segmento, independente de seus nomes. Por exemplo:

cue "/foo/bar/baz" cue "/foo/baz/quux" cue "/eggs/beans/toast" cue "/moog/synths/rule"

Entretanto, não irá corresponder a paths com menos ou mais segmentos. Os seguintes paths não serão localizados:

cue "/foo/bar" cue "/foo/baz/quux/quaax" cue "/eggs"

Cada * (asterisco) significa qualquer conteúdo. Então nós podemos encontrar paths com somente um segmento com /* ou paths com cinco segmentos com /*/*/*/*/*

Encontrando segmentos parciais

Se soubermos como o segmento irá começar ou terminar, nós podemos usar um asterisco * junto com o nome parcial do segmento. Por exemplo: "/foo/b*/baz" irá corresponder a qualquer path com três segmentos, onde o primeiro é foo, o último é baz e o segmento do meio pode ser qualquer coisa que comece com b. Então irá corresponder a:

cue "/foo/bar/baz" cue "/foo/baz/baz" cue "/foo/beans/baz"

Mas não irá corresponder a:

cue "/foo/flibble/baz" cue "/foo/abaz/baz" cue "/foo/beans/baz/eggs"

Você pode colocar o * no início do segmento para especificar os últimos caracteres de um segmento: "/foo/*zz/baz" que iráo corresponder a quaisquer 3 segmentos cue ou set onde o primeiro segmento for foo, o último for baz e o segmento do meio termine com zz, como "cue "/foo/whizz/baz".

Encontrando correspondências em segmentos de paths aninhados

As vezes você não sabe quantos segmentos do path você quer achar a correspondência. Nestes casos, você pode usar os poderosos duplos asteriscos: ** como em "/foo/**/baz" que irão corresponder a:

cue "/foo/bar/baz" cue "/foo/bar/beans/baz" cue "/foo/baz" cue "/foo/a/b/c/d/e/f/baz"

Correspondendo a Letras Sozinhas

Você pode usar o caracter ? para comparar com um caracter apenas, como em "/?oo/bar/baz" que irá corresponder a:

cue "/foo/bar/baz" cue "/goo/bar/baz" cue "/too/bar/baz" cue "/woo/bar/baz"

Correspondendo a Múltiplas Palavras

Se você sabe que um segmento pode ser uma palavra de um certo conjunto de palavras, você pode usar { e } para especificar uma lista de opções como em "/foo/{bar,beans,eggs}/quux" que irão corresponder aos seguintes padrões:

cue "/foo/bar/quux" cue "/foo/beans/quux" cue "/foo/eggs/quux"

Correspondendo a Múltiplas Letras

Por fim, você pode comparar com uma seleção de letras se você usar [ e ] para especificar uma lista de possibilidades como em "/foo/[abc]ux/baz" que irá corresponder somente a:

cue "/foo/aux/baz" cue "/foo/bux/baz" cue "/foo/cux/baz"

Você também pode utilizar o caracter - para especificar sequências de letras. Por exemplo"/foo/[a-e]ux/baz" que irá corresponder somente a:

cue "/foo/aux/baz" cue "/foo/bux/baz" cue "/foo/cux/baz" cue "/foo/dux/baz" cue "/foo/eux/baz"

Combinando Correspondências

Ao chamar sync ou get você está livre para combinar localizadores de correspondência em qualquer ordem que você achar necessário para encontrar qualquer evento Time State criado por cue ou set. Vamos ver um exemplo doido:

in_thread do
  sync "/?oo/[a-z]*/**/ba*/{quux,quaax}/"
  sample :loop_amen
end
sleep 1
cue "/foo/beans/a/b/c/d/e/bark/quux/"

Correspondência de Padrões OSC

Para os curiosos, estas regras de correspondência são baseados na especificação de correspondência de padrões do Open Sound Control (controle de som aberto), que é explicado com detalhes em: http://opensoundcontrol.org/spec-1_0


11 - MIDI

Uma vez que você tiver dominado a conversão de código em música, você pode pensar - o que vem depois? As vezes, as limitações de trabalhar puramente dentro da sintaxe e do sistema de som do Sonic Pi podem ser excitantes e te colocar em uma nova posição criativa. Entretanto, as vezes é essencial libertar o código para o mundo real. Não queremos duas coisas extras:

Sermos capazes de converter ações do mundo real em eventos codificáveis do Sonic Pi Sermos capazes de usar o forte modelo de temporização e semântica do Sonic Pi para controlar e manipular objetos no mundo real

Por sorte, existe um protocolo que está por aí desde os anos 80, que permite este tipo de interação - MIDI. Existe um número incrível de aparelhos externos, incluindo teclados, controladores, sequenciadores e software profissional que suportam MIDI. Nós podemos usar o MIDI para receber dados e também para enviar dados.

O Sonic Pi fornece suporte completo para o protocolo MIDI possibilitando que você conecte seu Código Ao Vivo com o mundo real. Vamos explorá-lo um pouco mais…


11.1 - Entrada MIDI

Nesta seção vamos aprender como conectar um controlador MIDI para enviar eventos para o Sonic Pi, para controlar nossos sintetizadores e sons. Vá e pegue um controlador MIDI, como um teclado ou tablet de controle e vamos começar!

Conectando um controlador MIDI

Para receber no Sonic Pi informações de um aparelho MIDI externo, primeiro nós precisamos conectá-lo ao computador. Tipicamente isso é feito através de uma conexão USB, apesar de que aparelhos antigos usam um conector DIN de 5 pinos, para o qual será necessário suporte de hardware em seu computador (por exemplo: algumas placas de áudio possuem conectores DIN). Uma vez que você tenha conectado seu aparelho, abra o Sonic Pi e dê uma olhada na seção IO (Entrada/Saída) do painel de Preferências. Você poderá ver seu aparelho listado lá. Caso não veja, tente o botão ‘Reiniciar MIDI’ e veja se aparece. Se ainda assim você não encontrar nada, a próxima coisa a tentar é consultar as configurações MIDI do seu sistema operacional para ver se ele enxerga seu aparelho. Se tudo isso falhar, sinta-se a vontade para perguntar na sala de chat: http://gitter.im/samaaron/sonic-pi

Recebendo eventos MIDI

Assim que seu aperelho esteja conectado, o Sonic Pi irá receber eventos automaticamente. Você mesmo pode confirmar manipulando seu aparelho MIDI e olhando no registro de Deixas (cue logger), na janela do app em baixo à direita, abaixo do registro (se isso não estiver visível, vá em Preferências > Editor > Mostrar & Ocultar e habilite a caixa ‘Mostrar o registro de deixas’). Você verá um fluxo de eventos como:

/midi/nanokey2_keyboard/0/1/note_off  [55, 64]
/midi/nanokey2_keyboard/0/1/note_on   [53, 102]
/midi/nanokey2_keyboard/0/1/note_off  [57, 64]
/midi/nanokey2_keyboard/0/1/note_off  [53, 64]
/midi/nanokey2_keyboard/0/1/note_on   [57, 87]
/midi/nanokey2_keyboard/0/1/note_on   [55, 81]
/midi/nanokey2_keyboard/0/1/note_on   [53, 96]
/midi/nanokey2_keyboard/0/1/note_off  [55, 64]

Quando você ver um fluxo de mensagens desta forma, você terá conectado com sucesso seu aparelho MIDI. Parabéns, vamos ver o que você consegue fazer com ele!

MIDI Time State

MIDI

Controlando o Código

Agora que você conectou seu aparelho MIDI, viu seus eventos no registro e descobriu que nosso conhecimento do Estado de Tempo (Time State) é tudo o que precisamos para trabalhar com eventos, podemos começar a nos divertir. Vamos construir um piano MIDI simples:

live_loop :midi_piano do 
  note, velocity = sync "/midi/nanokey2_keyboard/0/1/note_on"
  synth :piano, note: note
end

Temos algumas coisas acontecendo no código acima, incluindo alguns problemas. Primeiro, temos um live_loop simples, que irá se repetir para sempre, executando o código entre os blocos do/end. Isto foi apresentado na seção 9.2. Segundo, estamos chamando o comando sync para esperar pelo próximo evento Time State correspondente. Usamos uma string representando a mensagem MIDI que estamos procurando (que é a mesma que foi exibida no registro). Note que esta string longa é fornecida pela função autocompletar do Sonic Pi, desta forma você não precisa digitar tudo a mão. No registro nós vemos que haviam dois valorespara cada nota MIDI no evento, então nós atribuímos o resultado a duas variáveis note e velocity. Por fim, nós disparamos o sintetizador :piano passando nossa nota.

Agora tente você. Digite o código acima, substitua a chave de sincronização com uma string correspondente ao seu aparelho MIDI e clique em Executar. Pronto, você tem um piano funcionando! Entretanto, você provavelmente notará alguns problemas: primeiro, todas as notas possuem o mesmo volume, independente da força que você usa no teclado. Isso pode ser consertado facilmente usando o valor velocity do MIDI e convertendo-o para amplitude. Dado que MIDI possui uma variação entre 0 e 127, para converter este número para um valor entre 0 e 1 nós só precisamos dividi-lo por 127:

live_loop :midi_piano do 
  note, velocity = sync "/midi/nanokey2_keyboard/0/1/note_on"
  synth :piano, note: note, amp: velocity / 127.0
end

Atualize o código e clique em Executar novamente. Agora a força aplicada ao teclado é respeitada. Em seguida, vamos nos livrar desta pausa irritante.

Removendo a Latência

Antes de remover a pausa, nós precisamos entender porque ela acontece. Para manter todos os sintetizadores e Efeitos sincronizados através de uma variedade de CPUs com capacidades diferentes, o Sonic Pi agenda o áudio antecipadamente em 0.5s por padrão. (Note que esta latência adicional pode ser configurada pelas funções set_sched_ahead_time! e use_sched_ahead_time). Estes 0.5s de latência estão sendo adicionados ao disparador do nosso sintetizador :piano assim como está sendo adicionado a todos os sintetizadores disparados pelo Sonic Pi. Tipicamente nós realmente queremos manter esta latência, já que isso significa que todos os sintetizadores estarão sincronizados. Entretanto, isso só faz sentindo para sintetizadores disparados por código, usando play e sleep. No nosso caso, na realidade estamos disparando o sintetizador :piano pelo nosso aparelho MIDI e portanto não queremos que o Sonic Pi controle o tempo por nós. Podemos desligar esta latência com o comando use_real_time, que desabilita a latência para a thread atual. Isto significa que você pode usar o modo Real Time para loops ao vivo controlado por sync com aparelhos externos, e manter a latência padrâo para todos os outros loops. Vejamos:

live_loop :midi_piano do 
  use_real_time
  note, velocity = sync "/midi/nanokey2_keyboard/0/1/note_on"
  synth :piano, note: note, amp: velocity / 127.0
end

Atualize seu código para que ele corresponda ao código acima e clique em ‘Executar’ novamente. Agora nós temos um piano com baixa latência e sensibilidade variável, programado em apenas 5 linhas.

Recuperando valores

Finalmente, como nossos eventos MIDI estão indo diretamente para o Time State, nós também podemos usar a função get para recuperar o último valor recebido. Isso não bloqueia a thread atual e retorna nil se não houver valor para ser recuperado (o que você pode sobrescrever passando um valor padrão - veja a ajuda para get). Lembre-se que você pode chamar get em qualquer thread a qualquer momento para ver o valor mais recente correspondente ao Time State. Você pode até usar time_warp para voltar no tempo e chamar get para ver eventos passados…

Agora Você Está no Controle

O mais importante é que agora você pode usar as mesmas estruturas de código para sincronizar e recuperar (sync e get) informações MIDI de qualquer aparelho MIDI e fazer o que você quiser com os valores. Você pode agora escolher o que seu aparelho MIDI irá fazer!


11.2 - Saída MIDI

Além de receber eventos MIDI nõs podemos também enviar eventos MIDI para disparar e controlar hardware de sintetizadores externos, teclados e outros aparelho. O Sonic Pi fornece um conjunto completo de funções para enviar várias mensagens MIDI como:

Ligar nota - midi_note_on Desligar nota - midi_note_off Mudança de controle - midi_cc Pitch bend - midi_pitch_bend Tiques do relógio - midi_clock_tick

Também existem muitas outras mensagens MIDI suportadas - verifique a documentação da API para todas as outras funções que começam com midi_.

Conectando um aparelho MIDI

In order to send a MIDI message to an external device, we must first have connected it. Check out the subsection ‘Connecting a MIDI Controller’ in section 11.1 for further details. Note that if you’re using USB, connecting to a device which you’re sending to (rather than receiving from) is the same procedure. However, if you’re using the classic DIN connectors, make sure you connect to the MIDI out port of your computer. You should see your MIDI device listed in the preferences pane.

Enviando eventos MIDI

The many midi_* fns work just like play, sample and synth in that they send a message at the current (logical) time. For example, to spread out calls to the midi_* fns you need to use sleep just like you did with play. Let’s take a look:

midi_note_on :e3, 50

This will send a MIDI note on event to the connected MIDI device with velocity 50. (Note that Sonic Pi will automatically convert notes in the form :e3 to their corresponding MIDI number such as 52 in this case.)

If your connected MIDI device is a synthesiser, you should be able to hear it playing a note. To disable it use midi_note_off:

midi_note_off :e3

Selecionando um aparelho MIDI

By default, Sonic Pi will send each MIDI message to all connected devices on all MIDI channels. This is to make it easy to work with a single connected device without having to configure anything. However, sometimes a MIDI device will treat MIDI channels in a special way (perhaps each note has a separate channel) and also you may wish to connect more than one MIDI device at the same time. In more complicated setups, you may wish to be more selective about which MIDI device receives which message(s) and on which channel.

We can specify which device to send to using the port: opt, using the device name as displayed in the preferences:

midi_note_on :e3, port: "moog_minitaur"

We can also specify which channel to send to using the channel: opt (using a value in the range 1-16):

midi_note_on :e3, channel: 3

Of course we can also specify both at the same time to send to a specific device on a specific channel:

midi_note_on :e3, port: "moog_minitaur", channel: 5

MIDI Studio

Finally, a really fun thing to do is to connect the audio output of your MIDI synthesiser to one of the audio inputs of your soundcard. You can then control your synth with code using the midi_* fns and also manipulate the audio using live_audio and FX:

with_fx :reverb, room: 1 do
  live_audio :moog
end
live_loop :moog_trigger do
  midi (octs :e1, 3).tick, sustain: 0.1
  sleep 0.125
end

(The fn midi is available as a handy shortcut to sending both note on and note off events with a single command. Check out its documentation for further information).


12 - OSC

In addition to MIDI, another way to get information in and out of Sonic Pi is via the network using a simple protocol called OSC - Open Sound Control. This will let you send messages to and from external programs (both running on your computer and on external computers) which opens up the potential for control way beyond MIDI which has limitations due to its 1980s design.

For example, you could write a program in another programming language which sends and receives OSC (there are OSC libraries for pretty much every common language) and work directly with Sonic Pi. What you can use this for is only limited by your imagination.


12.1 - Receiving OSC

MIDI

A Basic OSC Listener

Let’s build a basic OSC listener:

live_loop :foo do
  use_real_time
  a, b, c = sync "/osc*/trigger/prophet"
  synth :prophet, note: a, cutoff: b, sustain: c
end

In this example we described an OSC path "/osc*/trigger/prophet" which we’re syncing on. This can be any valid OSC path (all letters and numbers are supported and the / is used like in a URL to break up the path to multiple words). The /osc prefix is added by Sonic Pi to all incoming OSC messages, so we need to send an OSC message with the path /trigger/prophet for our sync to stop blocking and the prophet synth to be triggered.

Sending OSC to Sonic Pi

We can send OSC to Sonic Pi from any programming language that has an OSC library. For example, if we’re sending OSC from Python we might do something like this:

from pythonosc import osc_message_builder
from pythonosc import udp_client
sender = udp_client.SimpleUDPClient('127.0.0.1', 4560)
sender.send_message('/trigger/prophet', [70, 100, 8])

Or, if we’re sending OSC from Clojure we might do something like this from the REPL:

(use 'overtone.core)
(def c (osc-client "127.0.0.1" 4560))
(osc-send c "/trigger/prophet" 70 100 8)

Receiving from External Machines

For security reasons, by default Sonic Pi does not let remote machines send it OSC messages. However, you can enable support for remote machines in Preferences->IO->Network->Receive Remote OSC Messages. Once you’ve enabled this, you can receive OSC messages from any computer on your network. Typically the sending machine will need to know your IP address (a unique identifier for your computer on your network - kind of like a phone number or an email address). You can discover the IP address of your computer by looking at the IO section of the preferences pane. (If your machine happens to have more than one IP address, hovering the mouse over the listed address will pop up with a list of all known addresses).

Note, some programs such as TouchOSC for iPhone and Android support sending OSC as a standard feature. So, once you’re listening to remote machines and know your IP address you can instantly start sending messages from apps like TouchOSC which enable you to build your own custom touch controls with sliders, buttons, dials etc. This can provide you with an enormous range of input options.


12.2 - Sending OSC

In addition to receiving OSC and working with it using Time State, we can also send out OSC messages in time with our music (just like we can send out MIDI messages in time with our music). We just need to know which IP address and port we’re sending to. Let’s give it a try:

use_osc "localhost", 4560
osc "/hello/world"

If you run the code above, you’ll notice that Sonic Pi is sending itself an OSC message! This is because we set the IP address to the current machine and the port to the default OSC in port. This is essentially the same as posting a letter to yourself - the OSC packet is created, leaves Sonic Pi, gets to the network stack of the operating system which then routes the packed back to Sonic Pi and then it’s received as a standard OSC message and is visible in the cue logger as the incoming message /osc:127.0.0.1:4560/hello/world. (Notice how Sonic Pi automatically prefixes all incoming OSC messages with /osc and then the hostname and port of the sender.)

Sending OSC to other programs

Of course, sending OSC messages to ourselves may be fun but it’s not that useful. The real benefit starts when we send messages to other programs:

use_osc "localhost", 123456
osc "/hello/world"

In this case we’re assuming there’s another program on the same machine listening to port 123456. If there is, then it will receive a "/hello/world OSC message with which it can do what it wants.

If our program is running on another machine, we need to know its IP address which we use instead of "localhost":

use_osc "192.168.10.23", 123456
osc "/hello/world"

Now we can send OSC messages to any device reachable to us via our local networks and even the internet!


13 - Multichannel Audio

So far, in terms of sound production, we’ve explored triggering synths and recorded sounds via the fns play, synth and sample. These have then generated audio which has played through our stereo speaker system. However, many computers also have the ability to input sound, perhaps through a microphone, in addition to the ability to send sound out to more than two speakers. Often, this capability is made possible through the use of an external sound card - these are available for all platforms. In this section of the tutorial we’ll take a look at how we can take advantage of these external sound cards and effortlessly work with multiple channels of audio in and out of Sonic Pi.


13.1 - Sound In

One simple (and perhaps familiar) way of accessing sound inputs is using our friend synth by specifying the :sound_in synth:

synth :sound_in

This will operate just like any synth such as synth :dsaw with the exception that the audio generated will be read directly from the first input of your system’s sound card. On laptops, this is typically the built-in microphone, but if you have an external sound card, you can plug any audio input to the first input.

Increasing the Duration

One thing you might notice is that just like synth :dsaw the :sound_in synth only lasts for 1 beat as it has a standard envelope. If you’d like to keep it open for a little longer, change the ADSR envelope settings. For example the following will keep the synth open for 8 beats before closing the connection:

synth :sound_in, sustain: 8

Adding FX

Of course, just like any normal synth, you can easily layer on effects with the FX block:

with_fx :reverb do
  with_fx :distortion do
    synth :sound_in, sustain: 8
  end
end

If you have plugged in a guitar to your first input, you should be able to hear it with distortion and reverb until the synth terminates as expected.

You are free to use the :sound_in synth as many times as you like concurrently (just like you would do with any normal synth). For example, the following will play two :sound_in synths at the same time - one through distortion and one through reverb:

with_fx :distortion do
  synth :sound_in, sustain: 8
end
with_fx :reverb do  
  synth :sound_in, sustain: 8
end

Multiple Inputs

You can select which audio input you want to play with the input: opt. You can also specify a stereo input (two consecutive inputs) using the :sound_in_stereo synth. For example, if you have a sound card with at least three inputs, you can treat the first two as a stereo stream and add distortion and the third as a mono stream and add reverb with the following code:

with_fx :distortion do
  synth :sound_in_stereo, sustain: 8, input: 1
end
with_fx :reverb do  
  synth :sound_in, sustain: 8, input: 3
end

Potential Issues

However, although this is a useful technique, there are a couple of limitations to this approach. Firstly, it only works for a specific duration (due to it having an ADSR envelope) and secondly, there’s no way to switch the FX around once the synth has been triggered. Both of these things are typical requests when working with external audio feeds such as microphones, guitars and external synthesisers. We’ll therefore take a look at Sonic Pi’s solution to the problem of manipulating a (potentially) infinite stream of live audio input: live_audio.


13.2 - Live Audio

The :sound_in synth as described in the previous section provides a very flexible and familiar method for working with input audio. However, as also discussed it has a few issues when working with a single input of audio as a single instrument (such as a voice or guitar). By far the best approach to working with a single continuous stream of audio is to use live_audio.

A Named Audio Input

live_audio shares a couple of core design constraints with live_loop (hence the similar name). Firstly it must have a unique name and secondly only one live_audio stream with that name may exist at any one time. Let’s take a look:

live_audio :foo

This code will act in a similar fashion to synth :sound_in with some key differences: it runs forever (until you explicitly stop it) and you can move it to new FX contexts dynamically.

Working with FX

On initial triggering live_audio works exactly as you might expect it to work with FX. For example, to start a live audio stream with added reverb simply use a :reverb FX block:

with_fx :reverb do
  live_audio :foo
end

However, given that live_audio runs forever (at least until you stop it) it would be pretty limiting if, like typical synths, the live audio was bound within the :reverb FX for its entire existence. Luckily this is not the case and it was designed to be easy to move between different FX. Let’s try it. Run the code above to hear live audio coming directly from the first input of your sound card. Note, if you’re using a laptop, this will typically be out of your built-in microphone, so it’s recommended to use headphones to stop feedback.

Now, whilst you’re still hearing the audio live from the sound card with reverb, change the code to the following:

with_fx :echo do
  live_audio :foo
end

Now, hit Run, and you’ll immediately hear the audio played through the echo FX and no longer through reverb. If you wanted them both, just edit the code again and hit Run:

with_fx :reverb do
  with_fx :echo do
    live_audio :foo
  end
end

It’s important to point out that you can call live_audio :foo from any thread or live loop and it will move the live audio synth to that thread’s current FX context. You could therefore easily have multiple live loops calling live_audio :foo at different times resulting in the FX context being automatically swapped around for some interesting results.

Stopping live audio

Unlike standard synths, as live_audio has no envelope, it will continue running forever (even if you delete the code, just like a function is still defined in memory if you delete the code in the editor). To stop it, you need to use the :stop arg:

live_audio :foo, :stop

It can easily be restarted by calling it without the :stop arg again:

live_audio :foo

Additionally all running live audio synths are stopped when you hit the global Stop button (as with all other running synths and FX).

Stereo input

With respect to audio channels, by default live_audio acts similarly to the :sound_in synth in that it takes a single mono input stream of audio and converts it to a stereo stream using the specified panning. However, just like :sound_in_stereo it’s also possible to tell live_audio to read two consecutive audio inputs and treat them as the left and right channels directly. This is achieved via the :stereo opt. For example, to treat input 2 as the left signal and input 3 as the right signal, you need to configure the input: opt to 2 and enable stereo mode as follows:

live_audio :foo, stereo: true, input: 2

Note that once you have started a live audio stream in stereo mode, you cannot change it to mono without stopping and starting. Similarly, if you start it in the default mono mode, you can’t switch to stereo without starting and stopping the stream.


13.3 - Sound Out

So far in this section we’ve looked at how to get multiple streams of audio into Sonic Pi - either through the use of the :sound_in synth or via the powerful live_audio system. In addition to working with multiple streams of input audio, Sonic Pi can also output multiple streams of audio. This is achieved via the :sound_out FX.

Output contexts

Let’s quickly recap on how Sonic Pi’s synths and FX output their audio to their current FX context. For example, consider the following:

with_fx :reverb do    # C
  with_fx :echo do    # B
    sample :bd_haus   # A
  end
end

The simplest way to understand what’s happening with the audio stream is to start at the innermost audio context and work our way out. In this case, the innermost context is labelled A and is the :bd_haus sample being triggered. The audio for this goes directly into its context which is B - the :echo FX. This then adds echo to the incoming audio and outputs it to its context which is C - the :reverb FX. This then adds reverb to the incoming audio and outputs to its context which is the top level - the left and right speakers (outputs 1 and 2 in your audio card). The audio flows outwards with a stereo signal all the way through.

Sound Out FX

The above behaviour is true for all synths (including live_audio) and the majority of FX with the exception of :sound_out. The :sound_out FX does two things. Firstly it outputs its audio to its external context as described above. Secondly it also outputs its audio directly to an output on your sound card. Let’s take a look:

with_fx :reverb do      # C
  with_fx :sound_out, output: 3 do # B
    sample :bd_haus     # A
  end
end

In this example, our :bd_haus sample outputs its audio to its external context which is the :sound_out FX. This in turn outputs its audio to its external context the :reverb FX (as expected). However, it also outputs a mono mix to the 3rd output of the system’s soundcard. The audio generated within :sound_out therefore has two destinations - the :reverb FX and audio card output 3.

Mono and Stereo out

As we’ve seen, by default, the :sound_out FX outputs a mono mix of the stereo input to a specific channel in addition to passing the stereo feed to the outer context (as expected). If outputting a mono mix isn’t precisely what you want to do, there are a number of alternative options. Firstly, by using the mode: opt you can choose to output just the left or just the right input signal to the audio card. Or you can use the :sound_out_stereo FX to output to two consecutive sound card outputs. See the function documentation for more information and examples.

Direct Out

As we have also seen, the default behaviour for :sound_out and :sound_out_stereo is to send the audio both to their external context (as is typical of all FX) and to the specified output on your soundcard. However, occasionally you may wish to only send to the output on your soundcard and not to the external context (and therefore not have any chance of the sound being mixed and sent to the standard output channels 1 and 2). This is possible by using the standard FX opt amp: which operates on the audio after the FX has been able to manipulate the audio:

with_fx :sound_out, output: 3, amp: 0 do # B
  sample :loop_amen                      # A
end

In the above example, the :loop_amen sample is sent to its outer context, the :sound_out FX. This then sends a mono mix to audio card output 3 and then multiplies the audio by 0 which essentially silences it. It is this silenced signal which is then sent out to the :sound_out’s outer context which is the standard output. Therefore with this code, the default output channels will not receive any audio, and channel 3 will receive a mono mix of the amen drum break.


14 - Conclusions

This concludes the Sonic Pi introductory tutorial. Hopefully you’ve learned something along the way. Don’t worry if you feel you didn’t understand everything - just play and have fun and you’ll pick things up in your own time. Feel free to dive back in when you have a question that might be covered in one of the sections.

If you have any questions that haven’t been covered in the tutorial, then please jump onto the Sonic Pi community forums and ask your question there. You’ll find someone friendly and willing to lend a hand.

Finally, I also invite you to take a deeper look at the rest of the documentation in this help system. There are a number of features that haven’t been covered in this tutorial that are waiting for your discovery.

So play, have fun, share your code, perform for your friends, show your screens and remember:

There are no mistakes, only opportunities.

Sam Aaron


- MagPi Articles

Appendix A collects all the Sonic Pi articles written for the MagPi magazine.

Dive into Topics

These articles aren’t meant to be read in any strict order and contain a lot of cross-over material from the tutorial itself. Rather than try and teach you all of Sonic Pi, they instead each focus on a specific aspect of Sonic Pi and cover it in a fun and accessible way.

Read the MagPi

You can see them in their glorious professionally typeset form in the free PDF downloads of The MagPi here: https://www.raspberrypi.org/magpi/

Suggest a Topic

If you don’t see a topic that interests you covered in these articles - why not suggest one? The easiest way to do that is to tweet your suggestion to @Sonic_Pi. You never know - your suggestion might be the subject of the next article!


- Five Top Tips

1. There are no mistakes

The most important lesson to learn with Sonic Pi is that there really are no mistakes. The best way to learn is to just try and try and try. Try lots of different things out, stop worrying whether your code sounds good or not and start experimenting with as many different synths, notes, FX and opts as possible. You’ll discover a lot of things that make you laugh because they sound just awful and some real gems that sound truly amazing. Simply drop the things you don’t like and keep the things you do. The more ‘mistakes’ you allow yourself to make the quicker you’ll learn and discover your personal coding sound.

2. Use the FX

Say you’ve already mastered the Sonic Pi basics of making sounds with sample, play? What’s next? Did you know that Sonic Pi supports over 27 studio FX to change the sound of your code? FX are like fancy image filters in drawing programs except that instead of blurring or making something black and white, you can add things like reverb, distortion and echo to your sound. Think of it like sticking the cable from your guitar to an effects pedal of your choice and then into the amplifier. Luckily, Sonic Pi makes using FX really easy and requires no cables! All you need to do is to choose which section of your code you’d like the FX added to and wrap it with the FX code. Let’s look at an example. Say you had the following code:

sample :loop_garzul
16.times do
  sample :bd_haus
  sleep 0.5
end

If you wanted to add FX to the :loop_garzul sample, you’d just tuck it inside a with_fx block like this:

with_fx :flanger do
  sample :loop_garzul
end
16.times do
  sample :bd_haus
  sleep 0.5
end

Now, if you wanted to add FX to the bass drum, go and wrap that with with_fx too:

with_fx :flanger do
  sample :loop_garzul
end
with_fx :echo do
  16.times do
    sample :bd_haus
    sleep 0.5
  end
end

Remember, you can wrap any code within with_fx and any sounds created will pass through that FX.

3. Parameterise your synths

In order to really discover your coding sound you’ll soon want to know how to modify and control synths and FX. For example, you might want to change the duration of a note, add more reverb, or change the time between echoes. Luckily, Sonic Pi gives you an amazing level of control to do exactly this with special things called optional parameters or opts for short. Let’s take a quick look. Copy this code into a workspace and hit run:

sample :guit_em9

Ooh, a lovely guitar sound! Now, let’s start playing with it. How about changing its rate?

sample :guit_em9, rate: 0.5

Hey, what’s that rate: 0.5 bit I just added at the end? That’s called an opt. All of Sonic Pi’s synths and FX support them and there’s loads to play around with. They’re also available for FX too. Try this:

with_fx :flanger, feedback: 0.6 do
  sample :guit_em9
end

Now, try increasing that feedback to 1 to hear some crazy sounds! Read the docs for full details on all the many opts available to you.

1.1 Programação ao vivo

The best way to quickly experiment and explore Sonic Pi is to live code. This allows you to start off some code and continually change and tweak it whilst it’s still playing. For example, if you don’t know what the cutoff parameter does to a sample, just play around. Let’s have a try! Copy this code into one of your Sonic Pi workspaces:

live_loop :experiment do
  sample :loop_amen, cutoff: 70
  sleep 1.75
end

Now, hit run and you’ll hear a slightly muffled drum break. Now, change the cutoff: value to 80 and hit run again. Can you hear the difference? Try 90, 100, 110

Once you get the hang of using live_loops you’ll not turn back. Whenever I do a live coding gig I rely on live_loop as much as a drummer relies on their sticks. For more information about live coding check out Section 9 of the built-in tutorial.

5. Surf the random streams

Finally, one thing I love doing is cheating by getting Sonic Pi to compose things for me. A really great way to do this is using randomisation. It might sound complicated but it really isn’t. Let’s take a look. Copy this into a spare workspace:

live_loop :rand_surfer do
  use_synth :dsaw
  notes = (scale :e2, :minor_pentatonic, num_octaves: 2)
  16.times do
    play notes.choose, release: 0.1, cutoff: rrand(70, 120)
    sleep 0.125
  end
end

Now, when you play this, you’ll hear a constant stream of random notes from the scale :e2 :minor_pentatonic played with the :dsaw synth. “Wait, wait! That’s not a melody”, I hear you shout! Well, here’s the first part of the magic trick. Every time we go round the live_loop we can tell Sonic Pi to reset the random stream to a known point. This is a bit like going back in time in the TARDIS with the Doctor to a particular point in time and space. Let’s try it - add the line use_random_seed 1 to the live_loop:

live_loop :rand_surfer do
  use_random_seed 1
  use_synth :dsaw
  notes = (scale :e2, :minor_pentatonic, num_octaves: 2)
  16.times do
    play notes.choose, release: 0.1, cutoff: rrand(70, 120)
    sleep 0.125
  end
end

Now, every time the live_loop loops around, the random stream is reset. This means it chooses the same 16 notes every time. Hey presto! An instant melody. Now, here’s the really exciting bit. Change the seed value from 1 to another number. Say 4923. Wow! Another melody! So, just by changing one number (the random seed), you can explore as many melodic combinations as you can imagine! Now, that’s the magic of code.


- Programação ao vivo

The laser beams sliced through the wafts of smoke as the subwoofer pumped bass deep into the bodies of the crowd. The atmosphere was ripe with a heady mix of synths and dancing. However something wasn’t quite right in this nightclub. Projected in bright colours above the DJ booth was futuristic text, moving, dancing, flashing. This wasn’t fancy visuals, it was merely a projection of Sonic Pi running on a Raspberry Pi. The occupant of the DJ booth wasn’t spinning disks, he was writing, editing and evaluating code. Live. This is Live Coding.

Programação ao vivo

This may sound like a far fetched story from a futuristic night club but coding music like this is a growing trend and is often described as Live Coding (http://toplap.org). One of the recent directions this approach to music making has taken is the Algorave (http://algorave.com) - events where artists like myself code music for people to dance to. However, you don’t need to be in a nightclub to Live Code - with Sonic Pi v2.6+ you can do it anywhere you can take your Raspberry Pi and a pair of headphones or some speakers. Once you reach the end of this article, you’ll be programming your own beats and modifying them live. Where you go afterwards will only be constrained by your imagination.

Live Loop

The key to live coding with Sonic Pi is mastering the live_loop. Let’s look at one:

live_loop :beats do
  sample :bd_haus
  sleep 0.5
end

There are 4 core ingredients to a live_loop. The first is its name. Our live_loop above is called :beats. You’re free to call your live_loop anything you want. Go crazy. Be creative. I often use names that communicate something about the music they’re making to the audience. The second ingredient is the do word which marks where the live_loop starts. The third is the end word which marks where the live_loop finishes, and finally there is the body of the live_loop which describes what the loop is going to repeat - that’s the bit between the do and end. In this case we’re repeatedly playing a bass drum sample and waiting for half a beat. This produces a nice regular bass beat. Go ahead, copy it into an empty Sonic Pi buffer and hit run. Boom, Boom, Boom!.

Redefining On-the-fly

Ok, so what’s so special about the live_loop? So far it just seems like a glorified loop! Well, the beauty of live_loops is that you can redefine them on-the-fly. This means that whilst they’re still running, you can change what they do. This is the secret to live coding with Sonic Pi. Let’s have a play:

live_loop :choral_drone do
  sample :ambi_choir, rate: 0.4
  sleep 1
end

choose

Sleeping is important

One of the most important lessons about live_loops is that they need rest. Consider the following live_loop:

live_loop :infinite_impossibilities do
  sample :ambi_choir
end

If you try running this code, you’ll immediately see Sonic Pi complaining that the live_loop did not sleep. This is a safety system kicking in! Take a moment to think about what this code is asking the computer to do. That’s right, it’s asking the computer to play an infinite amount of choir samples in zero time. Without the safety system the poor computer will try and do this and crash and burn in the process. So remember, your live_loops must contain a sleep.

Combining Sounds

Music is full of things happening at the same time. Drums at the same time as bass at the same time as vocals at the same time as guitars… In computing we call this concurrency and Sonic Pi provides us with an amazingly simple way of playing things at the same time. Simply use more than one live_loop!

live_loop :beats do
  sample :bd_tek
  with_fx :echo, phase: 0.125, mix: 0.4 do
    sample  :drum_cymbal_soft, sustain: 0, release: 0.1
    sleep 0.5
  end
end
live_loop :bass do
  use_synth :tb303
  synth :tb303, note: :e1, release: 4, cutoff: 120, cutoff_attack: 1
  sleep 4
end

Here, we have two live_loops, one looping quickly making beats and another looping slowly making a crazy bass sound.

One of the interesting things about using multiple live_loops is that they each manage their own time. This means it’s really easy to create interesting polyrhythmical structures and even play with phasing Steve Reich style. Check this out:

# Steve Reich's Piano Phase
notes = (ring :E4, :Fs4, :B4, :Cs5, :D5, :Fs4, :E4, :Cs5, :B4, :Fs4, :D5, :Cs5)
live_loop :slow do
  play notes.tick, release: 0.1
  sleep 0.3
end
live_loop :faster do
  play notes.tick, release: 0.1
  sleep 0.295
end

Bringing it all together

In each of these tutorials, we’ll end with a final example in the form of a new piece of music which draws from all of the ideas introduced. Read this code and see if you can imagine what it’s doing. Then, copy it into a fresh Sonic Pi buffer and hit Run and actually hear what it sounds like. Finally, change one of the numbers or comment and uncomment things out. See if you can use this as a starting point for a new performance, and most of all have fun! See you next time…

with_fx :reverb, room: 1 do
  live_loop :time do
    synth :prophet, release: 8, note: :e1, cutoff: 90, amp: 3
    sleep 8
  end
end
live_loop :machine do
  sample :loop_garzul, rate: 0.5, finish: 0.25
  sample :loop_industrial, beat_stretch: 4, amp: 1
  sleep 4
end
live_loop :kik do
  sample :bd_haus, amp: 2
  sleep 0.5
end
with_fx :echo do
  live_loop :vortex do
    # use_random_seed 800
    notes = (scale :e3, :minor_pentatonic, num_octaves: 3)
    16.times do
      play notes.choose, release: 0.1, amp: 1.5
      sleep 0.125
    end
  end
end

- Coded Beats

One of the most exciting and disrupting technical developments in modern music was the invention of samplers. These were boxes that allowed you to record any sound into them and then manipulate and play back those sounds in many interesting ways. For example, you could take an old record, find a drum solo (or break), record it into your sampler and then play it back on repeat at half-speed to provide the foundation for your latest beats. This is how early hip-hop music was born and today it’s almost impossible to find electronic music that doesn’t incorporate samples of some kind. Using samples is a really great way of easily introducing new and interesting elements into your live coded performances.

So where can you get a sampler? Well you already have one - it’s your Raspberry Pi! The built-in live coding app Sonic Pi has an extremely powerful sampler built into its core. Let’s play with it!

The Amen Break

One of the most classic and recognisable drum break samples is called the Amen Break. It was first performed in 1969 in the song “Amen Brother” by the Winstons as part of a drum break. However, it was when it was discovered by early hip-hop musicians in the 80s and used in samplers that it started being heavily used in a wide variety of other styles such as drum and bass, breakbeat, hardcore techno and breakcore.

I’m sure you’re excited to hear that it’s also built right into Sonic Pi. Clear up a buffer and throw in the following code:

sample :loop_amen

Hit Run and boom! You’re listening to one of the most influential drum breaks in the history of dance music. However, this sample wasn’t famous for being played as a one-shot, it was built for being looped.

Beat Stretching

Let’s loop the Amen Break by using our old friend the live_loop introduced in this tutorial last month:

live_loop :amen_break do
  sample :loop_amen
  sleep 2
end

OK, so it is looping, but there’s an annoying pause every time round. That is because we asked it to sleep for 2 beats and with the default BPM of 60 the :loop_amen sample only lasts for 1.753 beats. We therefore have a silence of 2 - 1.753 = 0.247 beats. Even though it’s short, it’s still noticeable.

To fix this issue we can use the beat_stretch: opt to ask Sonic Pi to stretch (or shrink) the sample to match the specified number of beats.

Sonic Pi’s sample and synth fns give you a lot of control via optional parameters such as amp:, cutoff: and release:. However, the term optional parameter is a real mouthful so we just call them opts to keep things nice and simple.

live_loop :amen_break do
  sample :loop_amen, beat_stretch: 2
  sleep 2
end  

Now we’re dancing! Although, perhaps we want to speed it up or slow it down to suit the mood.

Playing with Time

OK, so what if we want to change styles to old school hip hop or breakcore? One simple way of doing this is to play with time - or in other words mess with the tempo. This is super easy in Sonic Pi - just throw in a use_bpm into your live loop:

live_loop :amen_break do
  use_bpm 30
  sample :loop_amen, beat_stretch: 2
  sleep 2
end 

Whilst you’re rapping over those slow beats, notice that we’re still sleeping for 2 and our BPM is 30, yet everything is in time. The beat_stretch opt works with the current BPM to make sure everything just works.

Now, here’s the fun part. Whilst the loop is still live, change the 30 in the use_bpm 30 line to 50. Woah, everything just got faster yet kept in time! Try going faster - up to 80, to 120, now go crazy and punch in 200!

Filtering

Now we can live loop samples, let’s look at some of the most fun opts provided by the sample synth. First up is cutoff: which controls the cutoff filter of the sampler. By default this is disabled but you can easily turn it on:

live_loop :amen_break do
  use_bpm 50
  sample :loop_amen, beat_stretch: 2, cutoff: 70
  sleep 2
end  

Go ahead and change the cutoff: opt. For example, increase it to 100, hit Run and wait for the loop to cycle round to hear the change in the sound. Notice that low values like 50 sound mellow and bassy and high values like 100 and 120 are more full-sounding and raspy. This is because the cutoff: opt will chop out the high frequency parts of the sound just like a lawn-mower chops off the top of the grass. The cutoff: opt is like the length setting - determining how much grass is left over.

Slicing

Another great tool to play with is the slicer FX. This will chop (slice) the sound up. Wrap the sample line with the FX code like this:

live_loop :amen_break do
  use_bpm 50
  with_fx :slicer, phase: 0.25, wave: 0, mix: 1 do
    sample :loop_amen, beat_stretch: 2, cutoff: 100
  end
  sleep 2
end

Notice how the sound bounces up and down a little more. (You can hear the original sound without the FX by changing the mix: opt to 0.) Now, try playing around with the phase: opt. This is the rate (in beats) of the slicing effect. A smaller value like 0.125 will slice faster and larger values like 0.5 will slice more slowly. Notice that successively halving or doubling the phase: opts val tends to always sound good. Finally, change the wave: opt to one of 0, 1, or 2 and hear how it changes the sound. These are the various wave shapes. 0 is a saw wave, (hard in, fade out) 1 is a square wave (hard in, hard out) and 2 is a triangle wave (fade in, fade out).

Bringing it all together

Finally, let’s go back in time and revisit the early Bristol drum and bass scene with this month’s example. Don’t worry too much about what all this means, just type it in, hit Run, then start live coding it by changing opt numbers and see where you can take it. Please do share what you create! See you next time…

use_bpm 100
live_loop :amen_break do
  p = [0.125, 0.25, 0.5].choose
  with_fx :slicer, phase: p, wave: 0, mix: rrand(0.7, 1) do
    r = [1, 1, 1, -1].choose
    sample :loop_amen, beat_stretch: 2, rate: r, amp: 2
  end
  sleep 2
end
live_loop :bass_drum do
  sample :bd_haus, cutoff: 70, amp: 1.5
  sleep 0.5
end
live_loop :landing do
  bass_line = (knit :e1, 3, [:c1, :c2].choose, 1)
  with_fx :slicer, phase: [0.25, 0.5].choose, invert_wave: 1, wave: 0 do
    s = synth :square, note: bass_line.tick, sustain: 4, cutoff: 60
    control s, cutoff_slide: 4, cutoff: 120
  end
  sleep 4
end

- Synth Riffs

Whether it’s the haunting drift of rumbling oscillators or the detuned punch of saw waves piercing through the mix, the lead synth plays an essential role on any electronic track. In last month’s edition of this tutorial series we covered how to code our beats. In this tutorial we’ll cover how to code up the three core components of a synth riff - the timbre, melody and rhythm.

OK, so power up your Raspberry Pi, crack open Sonic Pi v2.6+ and let’s make some noise!

Timbral Possibilities

An essential part of any synth riff is changing and playing with the timbre of the sounds. We can control the timbre in Sonic Pi in two ways - choosing different synths for a dramatic change and setting the various synth opts for more subtle modifications. We can also use FX, but that’s for another tutorial…

Let’s create a simple live loop where we continually change the current synth:

live_loop :timbre do
  use_synth (ring :tb303, :blade, :prophet, :saw, :beep, :tri).tick
  play :e2, attack: 0, release: 0.5, cutoff: 100
  sleep 0.5
end

Take a look at the code. We’re simply ticking through a ring of synth names (this will cycle through each of these in turn repeating the list over and over). We pass this synth name to the use_synth fn (function) which will change the live_loop’s current synth. We also play note :e2 (E at the second octave), with a release time of 0.5 beats (half a second at the default BPM of 60) and with the cutoff: opt set to 100.

Hear how the different synths have very different sounds even though they’re all playing the same note. Now experiment and have a play. Change the release time to bigger and smaller values. For example, change the attack: and release: opts to see how different fade in/out times have a huge impact on the sound. Finally change the cutoff: opt to see how different cutoff values also massively influence the timbre (values between 60 and 130 are good). See how many different sounds you can create by just changing a few values. Once you’ve mastered that, just head to the Synths tab in the Help system for a full list of all the synths and all the available opts each individual synth supports to see just how much power you have under your coding fingertips.

Timbre

Timbre is just a fancy word describing the sound of a sound. If you play the same note with different instruments such as a violin, guitar, or piano, the pitch (how high or low it sounds) would be the same, but the sound quality would be different. That sound quality - the thing which allows you to tell the difference between a piano and a guitar is the timbre.

Melodic Composition

Another important aspect to our lead synth is the choice of notes we want to play. If you already have a good idea, then you can simply create a ring with your notes in and tick through them:

live_loop :riff do
  use_synth :prophet
  riff = (ring :e3, :e3, :r, :g3, :r, :r, :r, :a3)
  play riff.tick, release: 0.5, cutoff: 80
  sleep 0.25
end

Here, we’ve defined our melody with a ring which includes both notes such as :e3 and rests represented by :r. We’re then using .tick to cycle through each note to give us a repeating riff.

Auto Melody

It’s not always easy to come up with a nice riff from scratch. Instead it’s often easier to ask Sonic Pi for a selection of random riffs and to choose the one you like the best. To do that we need to combine three things: rings, randomisation and random seeds. Let’s look at an example:

live_loop :random_riff do
  use_synth :dsaw
  use_random_seed 3
  notes = (scale :e3, :minor_pentatonic).shuffle
  play notes.tick, release: 0.25, cutoff: 80
  sleep 0.25
end

There’s a few things going on - let’s look at them in turn. First, we specify that we’re using random seed 3. What does this mean? Well, the useful thing is that when we set the seed, we can predict what the next random value is going to be - it’s the same as it was last time we set the seed to 3! Another useful thing to know is that shuffling a ring of notes works in the same way. In the example above we’re essentially asking for the ‘third shuffle’ in the standard list of shuffles - which is also the same every time as we’re always setting the random seed to the same value right before the shuffle. Finally we’re just ticking through our shuffled notes to play the riff.

Now, here’s where the fun starts. If we change the random seed value to another number, say 3000, we get an entirely different shuffling of the notes. So now it’s extremely easy to explore new melodies. Simply choose the list of notes we want to shuffle (scales are a great starting point) and then choose the seed we want to shuffle with. If we don’t like the melody, just change one of those two things and try again. Repeat until you like what you hear!

Pseudo Randomisation

Sonic Pi’s randomisation is not actually random it’s what’s called pseudo random. Imagine if you were to roll a dice 100 times and write down the result of each roll onto a piece of paper. Sonic Pi has the equivalent of this list of results which it uses when you ask for a random value. Instead of rolling an actual dice, it just picks the next value from the list. Setting the random seed is just jumping to a specific point in that list.

Finding your Rhythm

Another important aspect to our riff is the rhythm - when to play a note and when not to. As we saw above we can use :r in our rings to insert rests. Another very powerful way is to use spreads which we’ll cover in a future tutorial. Today we’ll use randomisation to help us find our rhythm. Instead of playing every note we can use a conditional to play a note with a given probability. Let’s take a look:

live_loop :random_riff do
  use_synth :dsaw
  use_random_seed 30
  notes = (scale :e3, :minor_pentatonic).shuffle
  16.times do
    play notes.tick, release: 0.2, cutoff: 90 if one_in(2)
    sleep 0.125
  end
end

A really useful fn to know is one_in which will give us a true or false value with the specified probability. Here, we’re using a value of 2 so on average one time every two calls to one_in it will return true. In other words, 50% of the time it will return true. Using higher values will make it return false more often introducing more space into the riff.

Notice that we’ve added some iteration in here with 16.times. This is because we only want to reset our random seed value every 16 notes so our rhythm repeats every 16 times. This doesn’t affect the shuffling as that is still done immediately after the seed is set. We can use the iteration size to alter the length of the riff. Try changing the 16 to 8 or even 4 or 3 and see how it affects the rhythm of the riff.

Bringing it all together

OK, so let’s combine everything we’ve learned together into one final example. See you next time!

live_loop :random_riff do
  #  uncomment to bring in:
  #  synth :blade, note: :e4, release: 4, cutoff: 100, amp: 1.5
  use_synth :dsaw
  use_random_seed 43
  notes = (scale :e3, :minor_pentatonic, num_octaves: 2).shuffle.take(8)
  8.times do
    play notes.tick, release: rand(0.5), cutoff: rrand(60, 130) if one_in(2)
    sleep 0.125
  end
end
 
live_loop :drums do
  use_random_seed 500
  16.times do
    sample :bd_haus, rate: 2, cutoff: 110 if rand < 0.35
    sleep 0.125
  end
end
 
live_loop :bd do
  sample :bd_haus, cutoff: 100, amp: 3
  sleep 0.5
end

- Acid Bass

It’s impossible to look through the history of electronic dance music without seeing the enormous impact of the tiny Roland TB-303 synthesiser. It’s the secret sauce behind the original acid bass sound. Those classic squealing and squelching TB-303 bass riffs can be heard from the early Chicago House scene through to more recent electronic artists such as Plastikman, Squarepusher and Aphex Twin.

Interestingly, Roland never intended for the TB-303 to be used in dance music. It was originally created as a practice aid for guitarists. They imagined that people would program them to play bass lines to jam along to. Unfortunately there were a number of problems: they were a little fiddly to program, didn’t sound particularly good as a bass-guitar replacement and were pretty expensive to buy. Deciding to cut their losses, Roland stopped making them after 10,000 units were sold and after a number of years sitting on guitarist’s shelves, they soon could be found in the windows of second hand shops. These lonely discarded TB-303s were waiting to be discovered by a new generation of experimenters who started using them in ways that Roland didn’t imagine to create new crazy sounds. Acid House was born.

Although getting your hands on an original TB-303 is not so easy you will be pleased to know that you can turn your Raspberry Pi into one using the power of Sonic Pi. Behold, fire up Sonic Pi and throw this code into an empty buffer and hit Run:

use_synth :tb303
play :e1

Instant acid bass! Let’s play around…

Squelch that Bass

First, let’s build a live arpeggiator to make things fun. In the last tutorial we looked at how riffs can just be a ring of notes that we tick through one after another, repeating when we get to the end. Let’s create a live loop that does exactly that:

use_synth :tb303
live_loop :squelch do
  n = (ring :e1, :e2, :e3).tick
  play n, release: 0.125, cutoff: 100, res: 0.8, wave: 0
  sleep 0.125
end

Take a look at each line.

On the first line we set the default synth to be tb303 with the use_synth fn. On line two we create a live loop called :squelch which will just loop round and round. Line three is where we create our riff - a ring of notes (E in octaves 1, 2, and 3) which we simply tick through with .tick. We define n to represent the current note in the riff. The equals sign just means to assign the value on the right to the name on the left. This will be different every time round the loop. The first time round, n will be set to :e1. The second time round it will be :e2, followed by :e3, and then back to :e1, cycling round forever. Line four is where we actually trigger our :tb303 synth. We’re passing a few interesting opts here: release:, cutoff:, res: and wave: which we’ll discuss below. Line five is our sleep - we’re asking the live loop to loop round every 0.125s or 8 times a second at the default BPM of 60. Line six is the end to the live loop. This just tells Sonic Pi where the end of the live loop is.

Whilst you’re still figuring out what’s going on, type in the code above and hit the Run button. You should hear the :tb303 kick into action. Now, this is where the action is: let’s start live coding.

Whilst the loop is still live, change the cutoff: opt to 110. Now hit the Run button again. You should hear the sound become a little harsher and more squelchy. Dial in 120 and hit run. Now 130. Listen how higher cutoff values make it sound more piercing and intense. Finally, drop it down to 80 when you feel like a rest. Then repeat as many times as you want. Don’t worry, I’ll still be here…

Another opt worth playing with is res:. This controls the level of resonance of the filter. A high resonance is characteristic of acid bass sounds. We currently have our res: set to 0.8. Try cranking it up to 0.85, then 0.9, and finally 0.95. You might find that a cutoff such as 110 or higher will make the differences easier to hear. Finally go crazy and dial in 0.999 for some insane sounds. At a res this high, you’re hearing the cutoff filter resonate so much it starts to make sounds of its own!

Finally, for a big impact on the timbre try changing the wave: opt to 1. This is the choice of source oscillator. The default is 0 which is a sawtooth wave. 1 is a pulse wave and 2 is a triangle wave.

Of course, try different riffs by changing the notes in the ring or even picking notes from scales or chords. Have fun with your first acid bass synth.

Deconstructing the TB-303

The design of the original TB-303 is actually pretty simple. As you can see from the following diagram there’s only 4 core parts.

TB-303 Design

First is the oscillator wave - the raw ingredients of the sound. In this case we have a square wave. Next there’s the oscillator’s amplitude envelope which controls the amp of the square wave through time. These are accessed in Sonic Pi by the attack:, decay:, sustain: and release: opts along with their level counterparts. For more information read Section 2.4 ‘Duration with Envelopes’ in the built-in tutorial. We then pass our enveloped square wave through a resonant low pass filter. This chops off the higher frequencies as well as having that nice resonance effect. Now this is where the fun starts. The cutoff value of this filter is also controlled by its own envelope! This means we have amazing control over the timbre of the sound by playing with both of these envelopes. Let’s take a look:

use_synth :tb303
with_fx :reverb, room: 1 do
  live_loop :space_scanner do
    play :e1, cutoff: 100, release: 7, attack: 1, cutoff_attack: 4, cutoff_release: 4
    sleep 8
  end
end

For each standard envelope opt, there’s a cutoff_ equivalent opt in the :tb303 synth. So, to change the cutoff attack time we can use the cutoff_attack: opt. Copy the code above into an empty buffer and hit Run. You’ll hear a crazy sound warble in and out. Now start to play. Try changing the cutoff_attack: time to 1 and then 0.5. Now try 8.

Notice that I’ve passed everything through a :reverb FX for extra atmosphere - try other FX to see what works!

Bringing it all together

Finally, here’s a piece I composed using the ideas in this tutorial. Copy it into an empty buffer, listen for a while and then start live coding your own changes. See what crazy sounds you can make with it! See you next time…

use_synth :tb303
use_debug false
 
with_fx :reverb, room: 0.8 do
  live_loop :space_scanner do
    with_fx :slicer, phase: 0.25, amp: 1.5 do
      co = (line 70, 130, steps: 8).tick
      play :e1, cutoff: co, release: 7, attack: 1, cutoff_attack: 4, cutoff_release: 4
      sleep 8
    end
  end
 
  live_loop :squelch do
    use_random_seed 3000
    16.times do
      n = (ring :e1, :e2, :e3).tick
      play n, release: 0.125, cutoff: rrand(70, 130), res: 0.9, wave: 1, amp: 0.8
      sleep 0.125
    end
  end
end

- Musical Minecraft

Hello and welcome back! In the previous tutorials we’ve focussed purely on the music possibilities of Sonic Pi - (turning your Raspberry Pi into a performance ready musical instrument). So far we’ve learned how to:

Live Code - changing the sounds on-the-fly, Code some huge beats, Generate powerful synth leads, Re-create the famous TB-303 acid-bass sound.

There’s so much more to show you (which we will explore in future editions). However, this month, let’s look at something Sonic Pi can do that you probably didn’t realise: control Minecraft.

Hello Minecraft World

OK, let’s get started. Boot up your Raspberry Pi, fire up Minecraft Pi and create a new world. Now start up Sonic Pi and re-size and move your windows so you can see both Sonic Pi and Minecraft Pi at the same time.

In a fresh buffer type the following:

mc_message "Hello Minecraft from Sonic Pi!"

Now, hit Run. Boom! Your message appeared in Minecraft! How easy was that? Now, stop reading this for a moment and play about with your own messages. Have fun!

Screen 0

Sonic Teleporter

Now let’s do some exploring. The standard option is to reach for the mouse and keyboard and start walking around. That works, but it’s pretty slow and boring. It would be far better if we had some sort of teleport machine. Well, thanks to Sonic Pi, we have one. Try this:

mc_teleport 80, 40, 100

Crikey! That was a long way up. If you weren’t in flying-mode then you would have fallen back down all the way to the ground. If you double-tap space to enter flying-mode and teleport again, you’ll stay hovering at the location you zap to.

Now, what do those numbers mean? We have three numbers which describe the coordinates of where in the world we want to go. We give each number a name - x, y and z:

x - how far left and right (80 in our example) y - how high we want to be (40 in our example) z - how far forward and back (100 in our example)

By choosing different values for x, y and z we can teleport anywhere in our world. Try it! Choose different numbers and see where you can end up. If the screen goes black it’s because you’ve teleported yourself under the ground or into a mountain. Just choose a higher y value to get back out above land. Keep on exploring until you find somewhere you like…

Using the ideas so far, let’s build a Sonic Teleporter which makes a fun teleport sound whilst it whizzes us across the Minecraft world:

mc_message "Preparing to teleport...."
sample :ambi_lunar_land, rate: -1
sleep 1
mc_message "3"
sleep 1
mc_message "2"
sleep 1
mc_message "1"
sleep 1
mc_teleport 90, 20, 10
mc_message "Whoooosh!"

Screen 1

Magic Blocks

Now you’ve found a nice spot, let’s start building. You could do what you’re used to and start clicking the mouse furiously to place blocks under the cursor. Or you could use the magic of Sonic Pi. Try this:

x, y, z = mc_location
mc_set_block :melon, x, y + 5, z

Now look up! There’s a melon in the sky! Take a moment to look at the code. What did we do? On line one we grabbed the current location of Steve as the variables x, y and z. These correspond to our coordinates described above. We use these coordinates in the fn mc_set_block which will place the block of your choosing at the specified coordinates. In order to make something higher up in the sky we just need to increase the y value which is why we add 5 to it. Let’s make a long trail of them:

live_loop :melon_trail do
  x, y, z = mc_location
  mc_set_block :melon, x, y-1, z
  sleep 0.125
end

Now, jump over to Minecraft, make sure you’re in flying-mode (double tap space if not) and fly all around the world. Look behind you to see a pretty trail of melon blocks! See what kind of twisty patterns you can make in the sky.

Live Coding Minecraft

Those of you that have been following this tutorial over the last few months will probably have your minds blown at this point. The trail of melons is pretty cool, but the most exciting part of the previous example is that you can use live_loop with Minecraft! For those that don’t know, live_loop is Sonic Pi’s special magic ability that no other programming language has. It lets you run multiple loops at the same time and allows you to change them whilst they run. They are incredibly powerful and amazing fun. I use live_loops to perform music in nightclubs with Sonic Pi - DJs use discs and I use live_loops :-) However, today we’re going to live code both music and Minecraft.

Let’s get started. Run the code above and start making your melon trail again. Now, without stopping the code, just simply change :melon to :brick and hit run. Hey presto, you’re now making a brick trail. How simple was that! Fancy some music to go with it? Easy. Try this:

live_loop :bass_trail do
  tick
  x, y, z = mc_location
  b = (ring :melon, :brick, :glass).look
  mc_set_block b, x, y -1, z
  note = (ring :e1, :e2, :e3).look
  use_synth :tb303
  play note, release: 0.1, cutoff: 70
  sleep 0.125
end

Now, whilst that’s playing start changing the code. Change the block types - try :water, :grass or your favourite block type. Also, try changing the cutoff value from 70 to 80 and then up to 100. Isn’t this fun?

Bringing it all together

Screen 2

Let’s combine everything we’ve seen so far with a little extra magic. Let’s combine our teleportation ability with block placing and music to make a Minecraft Music Video. Don’t worry if you don’t understand it all, just type it in and have a play by changing some of the values whilst it’s running live. Have fun and see you next time…

live_loop :note_blocks do
  mc_message "This is Sonic Minecraft"
  with_fx :reverb do
    with_fx :echo, phase: 0.125, reps: 32 do
      tick
      x = (range 30, 90, step: 0.1).look
      y = 20
      z = -10
      mc_teleport x, y, z
      ns = (scale :e3, :minor_pentatonic)
      n = ns.shuffle.choose
      bs = (knit :glass, 3, :sand, 1)
      b = bs.look
      synth :beep, note: n, release: 0.1
      mc_set_block b, x+20, n-60+y, z+10
      mc_set_block b, x+20, n-60+y, z-10
      sleep 0.25
    end
  end
end
live_loop :beats do
  sample :bd_haus, cutoff: 100
  sleep 0.5
end

- Bizet Beats

After our brief excursion to the fantastic world of coding Minecraft with Sonic Pi last month, let’s get musical again. Today we’re going to bring a classical operatic dance piece straight into the 21st century using the awesome power of code.

Outrageous and Disruptive

Let’s jump into a time machine back to the year 1875. A composer called Bizet had just finished his latest opera Carmen. Unfortunately like many exciting and disruptive new pieces of music people initially didn’t like it at all because it was too outrageous and different. Sadly Bizet died ten years before the opera gained huge international success and became one of the most famous and frequently performed operas of all time. In sympathy with this tragedy let’s take one of the main themes from Carmen and convert it to a modern format of music that is also too outrageous and different for most people in our time - live coded music!

Decoding the Habanera

Trying to live code the whole opera would be a bit of a challenge for this tutorial, so let’s focus on one of the most famous parts - the bass line to the Habanera:

Habanera Riff

This may look extremely unreadable to you if you haven’t yet studied music notation. However, as programmers we see music notation as just another form of code - only it represents instructions to a musician instead of a computer. We therefore need to figure out a way of decoding it.

Notes

The notes are arranged from left to right like the words in this magazine but also have different heights. The height on the score represents the pitch of the note. The higher the note on the score, the higher the pitch of the note.

In Sonic Pi we already know how to change the pitch of a note - we either use high or low numbers such as play 75 and play 80 or we use the note names: play :E and play :F. Luckily each of the vertical positions of the musical score represents a specific note name. Take a look at this handy look up table:

Notes

Rests

Music scores are an extremely rich and expressive kind of code capable of communicating many things. It therefore shouldn’t come as much of a surprise that musical scores can not only tell you what notes to play but also when not to play notes. In programming this is pretty much equivalent to the idea of nil or null - the absence of a value. In other words not playing a note is like the absence of a note.

If you look closely at the score you’ll see that it’s actually a combination of black dots with lines which represent notes to play and squiggly things which represent the rests. Luckily Sonic Pi has a very handy representation for a rest: :r, so if we run: play :r it actually plays silence! We could also write play :rest, play nil or play false which are all equivalent ways of representing rests.

Rhythm

Finally, there’s one last thing to learn how to decode in the notation - the timings of the notes. In the original notation you’ll see that the notes are connected with thick lines called beams. The second note has two of these beams which means it lasts for a 16th of a beat. The other notes have a single beam which means they last for an 8th of a beat. The rest has two squiggly beams which means it also represents a 16th of the beat.

When we attempt to decode and explore new things a very handy trick is to make everything as similar as possible to try and see any relationships or patterns. For example, when we re-write our notation purely in 16ths you can see that our notation just turns into a nice sequence of notes and rests.

Habanera Riff 2

Re-coding the Habanera

We’re now in a position to start translating this bass line to Sonic Pi. Let’s encode these notes and rests in a ring:

(ring :d, :r, :r, :a, :f5, :r, :a, :r)

Let’s see what this sounds like. Throw it in a live loop and tick through it:

live_loop :habanera do
  play (ring :d, :r, :r, :a, :f5, :r, :a, :r).tick
  sleep 0.25
end

Fabulous, that instantly recognisable riff springs to life through your speakers. It took a lot of effort to get here, but it was worth it - high five!

Moody Synths

Now we have the bass line, let’s re-create some of the ambience of the operatic scene. One synth to try out is :blade which is a moody 80s style synth lead. Let’s try it with the starting note :d passed through a slicer and reverb:

live_loop :habanera do
  use_synth :fm
  use_transpose -12
  play (ring :d, :r, :r, :a, :f5, :r, :a, :r).tick
  sleep 0.25
end
with_fx :reverb do
  live_loop :space_light do
    with_fx :slicer, phase: 0.25 do
      synth :blade, note: :d, release: 8, cutoff: 100, amp: 2
    end
    sleep 8
  end
end

Now, try the other notes in the bass line: :a and :f5. Remember, you don’t need to hit stop, just modify the code whilst the music is playing and hit run again. Also, try different values for the slicer’s phase: opt such as 0.5, 0.75 and 1.

Bringing it all together

Finally, let’s combine all the ideas so far into a new remix of the Habanera. You might notice that I’ve included another part of the bass line as a comment. Once you’ve typed it all into a fresh buffer hit Run to hear the composition. Now, without hitting stop, uncomment the second line by removing the # and hit run again - how marvellous is that! Now, start mashing it around yourself and have fun.

use_debug false
bizet_bass = (ring :d, :r, :r, :a, :f5, :r, :a, :r)
#bizet_bass = (ring :d, :r, :r, :Bb, :g5, :r, :Bb, :r)
 
with_fx :reverb, room: 1, mix: 0.3 do
  live_loop :bizet do
    with_fx :slicer, phase: 0.125 do
      synth :blade, note: :d4, release: 8,
        cutoff: 100, amp: 1.5
    end
    16.times do
      tick
      play bizet_bass.look, release: 0.1
      play bizet_bass.look - 12, release: 0.3
      sleep 0.125
    end
  end
end
 
live_loop :ind do
  sample :loop_industrial, beat_stretch: 1,
    cutoff: 100, rate: 1
  sleep 1
end
 
live_loop :drums do
  sample :bd_haus, cutoff: 110
  synth :beep, note: 49, attack: 0,
    release: 0.1
  sleep 0.5
end

- Become a Minecraft VJ

Screen 0

Everyone has played Minecraft. You will all have built amazing structures, designed cunning traps and even created elaborate cart lines controlled by redstone switches. How many of you have performed with Minecraft? We bet you didn’t know that you could use Minecraft to create amazing visuals just like a professional VJ.

If your only way of modifying Minecraft was with the mouse, you’d have a tough time changing things fast enough. Luckily for you your Raspberry Pi comes with a version of Minecraft that can be controlled with code. It also comes with an app called Sonic Pi which makes coding Minecraft not only easy but also incredibly fun.

In today’s article we’ll be showing you some of the tips and tricks that we’ve used to create performances in night clubs and music venues around the world.

Let’s get started…

Getting Started

Let’s start with a simple warm up exercise to refresh ourselves with the basics. First up, crack open your Raspberry Pi and then fire up both Minecraft and Sonic Pi. In Minecraft, create a new world, and in Sonic Pi choose a fresh buffer and write in this code:

mc_message "Let's get started..."

Hit the Run button and you’ll see the message over in the Minecraft window. OK, we’re ready to start, let’s have some fun……

Sand Storms

When we’re using Minecraft to create visuals we try and think about what will both look interesting and also be easy to generate from code. One nice trick is to create a sand storm by dropping sand blocks from the sky. For that all we need are a few basic fns:

sleep - for inserting a delay between actions mc_location - to find our current location mc_set_block- to place sand blocks at a specific location rrand - to allow us to generate random values within a range live_loop - to allow us to continually make it rain sand

If you’re unfamiliar with any of the built-in fns such as rrand, just type the word into your buffer, click on it and then hit the keyboard combo Control-i to bring up the built-in documentation. Alternatively you can navigate to the lang tab in the Help system and then look up the fns directly along with all the other exciting things you can do.

Let’s make it rain a little first before unleashing the full power of the storm. Grab your current location and use it to create a few sand blocks up in the sky nearby:

x, y, z = mc_location
mc_set_block :sand, x, y + 20, z + 5
sleep 2
mc_set_block :sand, x, y + 20, z + 6
sleep 2
mc_set_block :sand, x, y + 20, z + 7
sleep 2
mc_set_block :sand, x, y + 20, z + 8

When you hit Run, you might have to look around a little as the blocks may start falling down behind you depending on which direction you’re currently facing. Don’t worry, if you missed them just hit Run again for another batch of sand rain - just make sure you’re looking the right way!

Let’s quickly review what’s going on here. On the first line we grabbed Steve’s location as coordinates with the fn mc_location and placed them into the vars x, y, and z. Then on the next lines we used the mc_set_block fn to place some sand at the same coordinates as Steve but with some modifications. We chose the same x coordinate, a y coordinate 20 blocks higher and then successively larger z coordinates so the sand dropped in a line away from Steve.

Why don’t you take that code and start playing around with it yourself? Try adding more lines, changing the sleep times, try mixing :sand with :gravel and choose different coordinates. Just experiment and have fun!

Live Loops Unleashed

OK, it’s time to get the storm raging by unleashing the full power of the live_loop - Sonic Pi’s magical ability which unleashes the full power of live coding - changing code on-the-fly whilst it’s running!

live_loop :sand_storm do
  x, y, z = mc_location
  xd = rrand(-10, 10)
  zd = rrand(-10, 10)
  co = rrand(70, 130)
  synth :cnoise, attack: 0, release: 0.125, cutoff: co
  mc_set_block :sand, x + xd, y+20, z+zd
  sleep 0.125
end

What fun! We’re looping round pretty quickly (8 times a second) and during each loop we’re finding Steve’s location like before but then generating 3 random values:

xd - the difference for x which will be between -10 and 10 zd - the difference for z also between -10 and 10 co - a cutoff value for the low pass filter between 70 and 130

We then use those random values in the fns synth and mc_set_block giving us sand falling in random locations around Steve along with a percussive rain-like sound from the :cnoise synth.

For those of you new to live loops - this is where the fun really starts with Sonic Pi. Whilst the code is running and the sand is pouring down, try changing one of the values, perhaps the sleep time to 0.25 or the :sand block type to :gravel. Now hit run again. Hey Presto! Things changed without the code stopping. This is your gateway to performing like a real VJ. Keep practising and changing things around. How different can you make the visuals without stopping the code?

Epic Block Patterns

Screensman 1

Finally, another great way of generating interesting visuals is to generate huge patterned walls to fly towards and close by. For this effect we’ll need to move from placing the blocks randomly to placing them in an ordered manner. We can do this by nesting two sets of iteration (hit the Help button and navigate to section 5.2 of the tutorial “Iteration and Loops” for more background on iteration). The funny |xd| after the do means that xd will be set for each value of the iteration. So the first time it will be 0, then 1, then 2… etc. By nesting two lots of iteration together like this we can generate all the coordinates for a square. We can then randomly choose block types from a ring of blocks for an interesting effect:

x, y, z = mc_location
bs = (ring :gold, :diamond, :glass)
10.times do |xd|
  10.times do |yd|
    mc_set_block bs.choose, x + xd, y + yd, z
  end
end

Pretty neat. Whilst we’re having fun here, try changing bs.choose to bs.tick to move from a random pattern to a more regular one. Try changing the block types and the more adventurous of you might want to try sticking this within a live_loop so that the patterns keep changing automatically.

Now, for the VJ finale - change the two 10.times to 100.times and hit Run. Kaboom! A Huge gigantic wall of randomly placed bricks. Imagine how long it would take you to build that manually with your mouse! Double-tap space to enter fly-mode and start swooping by for some great visual effects. Don’t stop here though - use your imagination to conjure up some cool ideas and then use the coding power of Sonic Pi to make it real. When you’ve practised enough dim the lights and put on a VJ show for your friends!


- Surfing Random Streams

Back in episode 4 of this tutorial series we took a brief look at randomisation whilst coding up some sizzling synth riffs. Given that randomisation is such an important part of my live coding DJ sets I thought it would be useful to cover the fundamentals in much greater detail. So, get your lucky hat on and let’s surf some random streams!

There is no random

The first thing to learn which might really surprise you when playing with Sonic Pi’s randomisation functions is that they’re not actually really random. What does this actually mean? Well, let’s try a couple of tests. First, imagine a number in your head between 0 and 1. Keep it there and don’t tell me. Now let me guess… was it 0.321567? No? Bah, I’m clearly no good at this. Let me have another go, but let’s ask Sonic Pi to choose a number this time. Fire up Sonic Pi v2.7+ and ask it for a random number but again don’t tell me:

print rand

Now for the reveal… was it 0.75006103515625? Yes! Ha, I can see you’re a little sceptical. Perhaps it was just a lucky guess. Let’s try again. Press the Run button again and see what we get… What? 0.75006103515625 again? This clearly can’t be random! You’re right, it’s not.

What’s going on here? The fancy computer science word here is determinism. This just means that nothing is by chance and everything is destined to be. Your version of Sonic Pi is destined to always return 0.75006103515625 in the program above. This might sound pretty useless, but let me assure you that it’s one of the most powerful parts of Sonic Pi. If you stick at it you’ll learn how to rely on the deterministic nature of Sonic Pi’s randomisation as a fundamental building block for your compositions and live coded DJ sets.

A Random Melody

When Sonic Pi boots it actually loads into memory a sequence of 441,000 pre-generated random values. When you call a random function such as rand or rrand, this random stream is used to generate your result. Each call to a random function consumes a value from this stream. Therefore the 10th call to a random function will use the 10th value from the stream. Also, every time you press the Run button, the stream is reset for that run. This is why I could predict the result to rand and why the ‘random’ melody was the same every time. Everybody’s version of Sonic Pi uses the exact same random stream which is very important when we start sharing our pieces with each other.

Let’s use this knowledge to generate a repeatable random melody:

8.times do
 play rrand_i(50, 95)
 sleep 0.125
end

Type this into a spare buffer and hit Run. You’ll hear a melody consisting of ‘random’ notes between 50 and 95. When it’s finished, hit Run again to hear exactly the same melody again.

Handy Randomisation Functions

Sonic Pi comes with a number of useful functions for working with the random stream. Here’s a list of some of the most useful:

rand - Simply returns the next value in the random stream rrand - Returns a random value within a range rrand_i - Returns a random whole number within a range one_in - Returns true or false with the given probability dice - Imitates rolling a dice and returns a value between 1 and 6 choose - Chooses a random value from a list

Check out their documentation in the Help system for detailed information and examples.

Resetting the Stream

Whilst the ability to repeat a sequence of chosen notes is essential to allow you to replay a riff on the dance floor, it might not be exactly the riff you want. Wouldn’t it be great if we could try a number of different riffs and choose the one we liked best? This is where the real magic starts.

We can manually set the stream with the fn use_random_seed. In Computer Science, a random seed is the starting point from which a new stream of random values can sprout out and blossom. Let’s try it:

use_random_seed 0
3.times do
  play rrand_i(50, 95)
  sleep 0.125
end

Great, we get the first three notes of our random melody above: 84, 83 and 71. However, we can now change the seed to something else. How about this:

use_random_seed 1
3.times do
  play rrand_i(50, 95)
  sleep 0.125
end

Interesting, we get 83, 71 and 61 . You might notice that the first two numbers here are the same as the last two numbers before - this isn’t a coincidence.

Remember that the random stream is just a giant list of ‘pre-rolled’ values. Using a random seed simply jumps us to a point in that list. Another way of thinking about it is to imagine a huge deck of pre-shuffled cards. Using a random seed is cutting the deck at a particular point. The fabulous part of this is that it’s precisely this ability to jump around the random stream which gives us huge power when making music.

Let’s revisit our random melody of 8 notes with this new stream resetting power, but let’s also throw in a live loop so we can experiment live whilst it’s playing:

live_loop :random_riff do    
  use_random_seed 0
  8.times do
    play rrand_i(50, 95), release: 0.1
    sleep 0.125
  end
end

Now, whilst it’s still playing, change the seed value from 0 to something else. Try 100, what about 999. Try your own values, experiment and play around - see which seed generates the riff you like best.

Bringing it all together

This month’s tutorial has been quite a technical dive into the workings of Sonic Pi’s randomisation functionality. Hopefully it has given you some insight into how it works and how you can start using randomisation in a reliable way to create repeatable patterns within your music. It’s important to stress that you can use repeatable randomisation anywhere you want. For example, you can randomise the amplitude of notes, the timing of the rhythm, the amount of reverb, the current synth, the mix of an FX, etc. etc. In the future we’ll take a close look at some of these applications, but for now let me leave you with a short example.

Type the following into a spare buffer, hit Run, and then start changing the seeds around, hit Run again (whilst it’s still playing) and explore the different sounds, rhythms and melodies you can make. When you find a nice one, remember the seed number so you can get back to it. Finally, when you’ve found a few seeds you like, put on a live coded performance for your friends by simply switching between your favourite seeds to create a full piece.

live_loop :random_riff do
  use_random_seed 10300
  use_synth :prophet
  s = [0.125, 0.25, 0.5].choose
  8.times do
    r = [0.125, 0.25, 1, 2].choose
    n = (scale :e3, :minor).choose
    co = rrand(30, 100)
    play n, release: r, cutoff: co
    sleep s
  end
end
live_loop :drums do
  use_random_seed 2001
  16.times do
    r = rrand(0.5, 10)
    sample :drum_bass_hard, rate: r, amp: rand
    sleep 0.125
  end
end

- Controlling Your Sound

So far during this series we’ve focussed on triggering sounds. We’ve discovered that we can trigger the many synths built into Sonic Pi with play or synth and how to trigger pre-recorded samples with sample. We’ve also looked at how we can wrap these triggered sounds within studio FX such as reverb and distortion using the with_fx command. Combine this with Sonic Pi’s incredibly accurate timing system and you can produce a vast array of sounds, beats and riffs. However, once you’ve carefully selected a particular sound’s options and triggered it, there’s no ability to mess with it whilst it’s playing right? Wrong! Today you’re going to learn something very powerful - how to control running synths.

A Basic Sound

Let’s create a nice simple sound. Fire up Sonic Pi and in a fresh buffer type the following:

synth :prophet, note: :e1, release: 8, cutoff: 100

Now press the Run button at the top left to hear a lovely rumbling synth sound. Go ahead, press it again a few times to get a feel for it. OK, done? Let’s start controlling it!

Synth Nodes

A little known feature in Sonic Pi is that the fns play, synth and sample, return something called a SynthNode which represents a running sound. You can capture one of these SynthNodes using a standard variable and then control it at a later point in time. For example, let’s change the value of the cutoff: opt after 1 beat:

sn = synth :prophet, note: :e1, release: 8, cutoff: 100
sleep 1
control sn, cutoff: 130

Let’s look at each line in turn:

Firstly we trigger the :prophet synth using the synth fn as normal. However we also capture the result in a variable called sn. We could have called this variable something completely different such as synth_node or jane - the name doesn’t matter. However, it’s important to choose a name that’s meaningful to you for your performances and for people reading your code. I chose sn as it’s a nice short mnemonic for synth node.

On line 2 we have a standard sleep command. This does nothing special - it just asks the computer to wait for 1 beat before moving onto the next line.

Line 3 is where the control fun starts. Here, we use the control fn to tell our running SynthNode to change the cutoff value to 130. If you hit the Run button, you’ll hear the :prophet synth start playing as before, but after 1 beat it will shift to sound a lot brighter.

Modulatable Options

Most of Sonic Pi’s synths and FX opts may be changed after being triggered. However, this isn’t the case for all of them. For example, the envelope opts attack:, decay:, sustain: and release: can only be set when triggering the synth. Figuring out which opts can and can’t be changed is simple - just head to the documentation for a given synth or FX and then scroll down to the individual option documentation and look for the phrases “May be changed whilst playing” or “Can not be changed once set”. For example, the documentation for the :beep synth’s attack: opt makes it clear that it’s not possible to change it:

Default: 0 Must be zero or greater Can not be changed once set Scaled with current BPM value

Multiple Changes

Whilst a synth is running you’re not limited to changing it only once - you’re free to change it as many times as you like. For example, we can turn our :prophet into a mini arpeggiator with the following:

notes = (scale :e3, :minor_pentatonic)
sn = synth :prophet, note: :e1, release: 8, cutoff: 100
sleep 1
16.times do
  control sn, note: notes.tick
  sleep 0.125
end

In this snippet of code we just added a couple of extra things. First we defined a new variable called notes which contains the notes we’d like to cycle through (an arpeggiator is just a fancy name for something that cycles through a list of notes in order). Secondly we replaced our single call to control with an iteration calling it 16 times. In each call to control we .tick through our ring of notes which will automatically repeat once we get to the end (thanks to the fabulous power of Sonic Pi’s rings). For a bit of variety try replacing .tick with .choose and see if you can hear the difference.

Note that we can change multiple opts simultaneously. Try changing the control line to the following and listen for the difference:

control sn, note: notes.tick, cutoff: rrand(70, 130)

Sliding

When we control a SynthNode, it responds exactly on time and instantly changes the value of the opt to the new one as if you’d pressed a button or flicked a switch requesting the change. This can sound rhythmical and percussive - especially if the opt controls an aspect of the timbre such as cutoff:. However, sometimes you don’t want the change to happen instantaneously. Instead, you might want to smoothly move from the current value to the new one as if you’d moved a slider or dial. Of course, Sonic Pi can also do this too using the _slide: opts.

Each opt that can be modified also has a special corresponding _slide: opt that allows you to specify a slide time. For example, amp: has amp_slide: and cutoff: has cutoff_slide:. These slide opts work slightly differently than all the other opts in that they tell the synth note how to behave next time they are controlled. Let’s take a look:

sn = synth :prophet, note: :e1, release: 8, cutoff: 70, cutoff_slide: 2
sleep 1
control sn, cutoff: 130

Notice how this example is exactly the same as before except with the addition of cutoff_slide:. This is saying that next time this synth has its cutoff: opt controlled, it will take 2 beats to slide from the current value to the new one. Therefore, when we use control you can hear the cutoff slide from 70 to 130. It creates an interesting dynamic feel to the sound. Now, try changing the cutoff_slide: time to a shorter value such as 0.5 or a longer value such as 4 to see how it changes the sound. Remember, you can slide any of the modifiable opts in exactly this way and each _slide: value can be totally different so you can have the cutoff sliding slowly, the amp sliding fast and the pan sliding somewhere in between if that’s what you’re looking to create…

Bringing it all together

Let’s look at a short example which demonstrates the power of controlling synths after they’ve been triggered. Notice that you can also slide FX just like synths although with a slightly different syntax. Check out section 7.2 of the built-in tutorial for more information on controlling FX.

Copy the code into a spare buffer and take a listen. Don’t stop there though - play around with the code. Change the slide times, change the notes, the synth, the FX and the sleep times and see if you can turn it into something completely different!

live_loop :moon_rise do
  with_fx :echo, mix: 0, mix_slide: 8 do |fx|
    control fx, mix: 1
    notes = (scale :e3, :minor_pentatonic, num_octaves: 2).shuffle
    sn = synth :prophet , sustain: 8, note: :e1, cutoff: 70, cutoff_slide: 8
    control sn, cutoff: 130
    sleep 2
    32.times do
      control sn, note: notes.tick, pan: rrand(-1, 1)
      sleep 0.125
    end
  end
end

- Tracking the Beat

Last month in this series we took a deep technical dive into the randomisation system underpinning Sonic Pi. We explored how we can use it to deterministically add new levels of dynamic control over our code. This month we’re going to continue our technical dive and turn our attention to Sonic Pi’s unique tick system. By the end of this article you’ll be ticking your way through rhythms and riffs on your way to being a live coding DJ.

Beat Counting

When making music we often want to do a different thing depending on which beat it is. Sonic Pi has a special beat counting system called tick to give you precise control over when a beat actually occurs and even supports multiple beats with their own tempos.

Let’s have a play - to advance the beat we just need to call tick. Open up a fresh buffer, type in the following and hit Run:

puts tick #=> 0

This will return the current beat: 0. Notice that even if you press the Run button a few times it will always return 0. This is because each run starts a fresh beat counting from 0. However, whilst the run is still active, we can advance the beat as many times as we want:

puts tick #=> 0
puts tick #=> 1
puts tick #=> 2

Whenever you see the symbol #=> at the end of a line of code it means that that line will log the text on the right-hand-side. For example, puts foo #=> 0 means the code puts foo prints 0 to the log at that point in the program.

Checking the Beat

We’ve seen that tick does two things. It increments (adds one) and returns the current beat. Sometimes we just want to look at the current beat without having to increment it which we can do via look:

puts tick #=> 0
puts tick #=> 1
puts look #=> 1
puts look #=> 1

In this code we tick the beat up twice and then call look twice. We’ll see the following values in the log: 0, 1, 1, 1. The first two ticks returned 0, then 1 as expected, then the two looks just returned the last beat value twice which was 1.

Rings

So now we can advance the beat with tick and check the beat with look. What next? We need something to tick over. Sonic Pi uses rings for representing riffs, melodies and rhythms and the tick system has been specifically designed to work very closely with them. In fact, rings have their own dot version of tick which does two things. Firstly, it acts like a regular tick and increments the beat. Secondly it looks up the ring value using the beat as the index. Let’s take a look:

puts (ring :a, :b, :c).tick #=> :a

.tick is a special dot version of tick which will return the first value of the ring :a. We can grab each of the values in the ring by calling .tick multiple times:

puts (ring :a, :b, :c).tick #=> :a
puts (ring :a, :b, :c).tick #=> :b
puts (ring :a, :b, :c).tick #=> :c
puts (ring :a, :b, :c).tick #=> :a
puts look                   #=> 3

Take a look at the log and you’ll see :a, :b, :c and then :a again. Notice that look returns 3. Calls to .tick act just like they are regular calls to tick - they increment the local beat.

Um Live Loop Arpegiador

O poder real surge quando você mistura tick com anéis (rings) e live_loops. Quando combinados nós temos todas as ferramentas que nós precisamos para tanto construir quanto entender um arpejador simples. Precisamos somente de 4 coisas:

Um anel (ring) contendo as notas que queremos arpejar. Uma forma de incrementar e obter a batida. A habilidade de tocar uma nota baseada na batida atual. Uma estrutura de loop para manter o arpejador repetindo.

Todos estes conceitos podem ser encontrados no seguinte código:

notes = (ring 57, 62, 55, 59, 64) 
live_loop :arp do
  use_synth :dpulse
  play notes.tick, release: 0.2
  sleep 0.125
end

Let’s look at each of these lines. First we define our ring of notes which we’ll continually play. We then create a live_loop called :arp which loops round for us. Each time round the live_loop we set our synth to :dpulse and then play the next note in our ring using .tick. Remember that this will increment our beat counter and use the latest beat value as an index into our notes ring. Finally, we wait for an eighth of a beat before looping round again.

Multiple Simultaneous Beats

A really important thing to know is that ticks are local to the live_loop. This means that each live_loop has its own independent beat counter. This is much more powerful than having a global metronome and beat. Let’s take a look at this in action:

notes = (ring 57, 62, 55, 59, 64) 
with_fx :reverb do
  live_loop :arp do
    use_synth :dpulse
    play notes.tick + 12, release: 0.1
    sleep 0.125
  end
end
live_loop :arp2 do
  use_synth :dsaw
  play notes.tick - 12, release: 0.2
  sleep 0.75
end

Clashing Beats

A big cause of confusion with Sonic Pi’s tick system is when people want to tick over multiple rings in the same live_loop:

use_bpm 300
use_synth :blade
live_loop :foo do
  play (ring :e1, :e2, :e3).tick
  play (scale :e3, :minor_pentatonic).tick
  sleep 1
end

Even though each live_loop has its own independent beat counter, we’re calling .tick twice within the same live_loop. This means that the beat will be incremented twice every time round. This can produce some interesting polyrhythms but is often not what you want. There are two solutions to this problem. One option is to manually call tick at the start of the live_loop and then use .look to look up the current beat in each live_loop. The second solution is to pass a unique name to each call to .tick such as .tick(:foo). Sonic Pi will then create and track a separate beat counter for each named tick you use. That way you can work with as many beats as you need! See the section on named ticks in 9.4 of the built-in tutorial for more information.

Bringing it all together

Let’s bring all this knowledge of ticks, rings and live_loops together for a final fun example. As usual, don’t treat this as a finished piece. Start changing things and play around with it and see what you can turn it into. See you next time…

use_bpm 240
notes = (scale :e3, :minor_pentatonic).shuffle
live_loop :foo do
  use_synth :blade
  with_fx :reverb, reps: 8, room: 1 do
    tick
    co = (line 70, 130, steps: 32).tick(:cutoff)
    play (octs :e3, 3).look, cutoff: co, amp: 2
    play notes.look, amp: 4
    sleep 1
  end
end 
live_loop :bar do
  tick
  sample :bd_ada if (spread 1, 4).look
  use_synth :tb303
  co = (line 70, 130, steps: 16).look
  r = (line 0.1, 0.5, steps: 64).mirror.look
  play notes.look, release: r, cutoff: co
  sleep 0.5
end

- Cortando Amostras

Way back in episode 3 of this Sonic Pi series we looked at how to loop, stretch and filter one of the most famous drum breaks of all time - the Amen Break. In this tutorial we’re going to take this one step further and learn how to slice it up, shuffle the slices and glue it back together in a completely new order. If that sounds a bit crazy to you, don’t worry, it will all become clear and you’ll soon master a powerful new tool for your live coded sets.

Sound as Data

Before we get started let’s just take a brief moment to understand how to work with samples. By now, you’ve all hopefully played with Sonic Pi’s powerful sampler. If not, there’s no time like the present! Boot up your Raspberry Pi, launch Sonic Pi from the Programming menu, type the following into a fresh buffer and then hit the Run button to hear a pre-recorded drum beat:

sample :loop_amen

A recording of a sound is simply represented as data - lots of numbers between -1 and 1 which represent the peaks and troughs of the sound wave. If we play those numbers back in order, we get the original sound. However, what’s to stop us from playing them back in a different order and creating a new sound?

How are samples actually recorded? It’s actually pretty simple once you understand the basic physics of sound. When you make a sound - for example by hitting a drum, the noise travels through the air in a similar fashion to how the surface of a lake ripples when you throw a pebble into it. When those ripples reach your ears, your eardrum moves sympathetically and converts those movements into the sound you hear. If we wish to record and play back the sound, we therefore need a way of capturing, storing and reproducing those ripples. One way is to use a microphone which acts like an eardrum and moves back and forth as the sound ripples hit it. The microphone then converts its position into a tiny electric signal which is then measured many times a second. These measurements are then represented as a series of numbers between -1 and 1.

If we were to plot a visualisation of the sound it would be a simple graph of data with time on the x axis and microphone/speaker position as a value between -1 and 1 on the y axis. You can see an example of such a graph at the top of the diagram.

Playing Part of a Sample

So, how do we code Sonic Pi to play a sample back in a different order? To answer this question we need to take a look at the start: and finish: opts for sample. These let us control the start and finish positions of our playback of the numbers which represent the sound. The values for both of these opts are represented as a number between 0 and 1 where 0 represents the start of the sample and 1 is the end. So, to play the first half of the Amen Break, we just need to specify a finish: of 0.5:

sample :loop_amen, finish: 0.5

We can add in a start: value to play an even smaller section of the sample:

sample :loop_amen, start: 0.25, finish: 0.5

For fun, you can even have the finish: opt’s value be before start: and it will play the section backwards:

sample :loop_amen, start: 0.5, finish: 0.25

Re-ordering Sample Playback

Now that we know that a sample is simply a list of numbers that can be played back in any order and also how to play a specific part of a sample we can now start having fun playing a sample back in the ‘wrong’ order.

Amen Slices

Let’s take our Amen Break and chop it up into 8 equally-sized slices and then shuffle the pieces around. Take a look at the diagram: at the top A) represents the graph of our original sample data. Chopping it into 8 slices gives us B) - notice that we’ve given each slice a different colour to help distinguish them. You can see each slice’s start and finish values at the top. Finally C) is one possible re-ordering of the slices. We can then play this back to create a new beat. Take a look at the code to do this:

live_loop :beat_slicer do
  slice_idx = rand_i(8)
  slice_size = 0.125
  s = slice_idx * slice_size
  f = s + slice_size
  sample :loop_amen, start: s, finish: f
  sleep sample_duration :loop_amen, start: s, finish: f
end

we choose a random slice to play which should be a random number between 0 and 7 (remember that we start counting at 0). Sonic Pi has a handy function for exactly this: rand_i(8). We then store this random slice index in the variable slice_idx. We define our slice_size which is 1/8 or 0.125. The slice_size is necessary for us to convert our slice_idx into a value between 0 and 1 so we can use it as our start: opt. We calculate the start position s by multiplying the slice_idx by the slice_size. We calculate the finish position f by adding the slice_size to the start position s. We can now play the sample slice by plugging in the s and f values into the start: and finish: opts for sample. Before we play the next slice we need to know how long to sleep which should be the duration of the sample slice. Luckily, Sonic Pi has us covered with sample_duration which accepts all the same opts as sample and simply returns the duration. Therefore, by passing sample_duration our start: and finish: opts, we can find out the duration of a single slice. We wrap all of this code in a live_loop so that we continue to pick new random slices to play.

Bringing it all together

Let’s combine everything we’ve seen so far into a final example which demonstrates how we can take a similar approach to combine randomly sliced beats with some bass to create the start of an interesting track. Now it’s your turn - take the code below as a starting point and see if you can take it in your own direction and create something new…

live_loop :sliced_amen do
  n = 8
  s =  line(0, 1, steps: n).choose
  f = s + (1.0 / n)
  sample :loop_amen, beat_stretch: 2, start: s, finish: f
  sleep 2.0  / n
end
live_loop :acid_bass do
  with_fx :reverb, room: 1, reps: 32, amp: 0.6 do
    tick
    n = (octs :e0, 3).look - (knit 0, 3 * 8, -4, 3 * 8).look
    co = rrand(70, 110)
    synth :beep, note: n + 36, release: 0.1, wave: 0, cutoff: co
    synth :tb303, note: n, release: 0.2, wave: 0, cutoff: co
    sleep (ring 0.125, 0.25).look
  end
end

- Code a Probabilistic Sequencer

In a previous episode of this Sonic Pi series we explored the power of randomisation to introduce variety, surprise and change into our live coded tracks and performances. For example, we randomly picked notes from a scale to create never-ending melodies. Today we’re going to learn a new technique which uses randomisation for rhythm - probabilistic beats!

Probability

Before we can start making new beats and synth rhythms we need to take a quick dive into the basics of probability. This might sound daunting and complicated, but really it’s just as simple as rolling a dice - honestly! When you take a regular 6 sided board game dice and roll it what’s actually happening? Well, firstly you’ll roll either a 1, 2, 3, 4, 5 or 6 with exactly the same chance of getting any of the numbers. In fact, given that it’s a 6 sided dice, on average (if you roll lots and lots of times) you’ll throw a 1 every 6 throws. This means you have a 1 in 6 chance of throwing a 1. We can emulate dice rolls in Sonic Pi with the fn dice. Let’s roll one 8 times:

8.times do
  puts dice
  sleep 1
end

Notice how the log prints values between 1 and 6 just as if we’d rolled a real dice ourselves.

Random Beats

Now imagine you had a drum and every time you were about to hit it you rolled a dice. If you rolled a 1, you hit the drum and if you rolled any other number you didn’t. You now have a probabilistic drum machine working with a probability of 1/6! Let’s hear what that sounds like:

live_loop :random_beat do
  sample :drum_snare_hard if dice == 1
  sleep 0.125
end

Let’s quickly go over each line to make sure everything is very clear. First we create a new live_loop called :random_beat which will continually repeat the two lines between do and end. The first of these lines is a call to sample which will play a pre-recorded sound (the :drum_snare_hard sound in this case). However, this line has a special conditional if ending. This means that the line will only be executed if the statement on the right hand side of the if is true. The statement in this case is dice == 1. This calls our dice function which, as we have seen, returns a value between 1 and 6. We then use the equality operator == to check to see if this value is 1. If it is 1, then the statement resolves to true and our snare drum sounds, if it isn’t 1 then the statement resolves to false and the snare is skipped. The second line simply waits for 0.125 seconds before rolling the dice again.

Changing Probabilities

Those of you that have played role play games will be familiar with lots of strangely shaped dice with different ranges. For example there is the tetrahedron shaped dice which has 4 sides and even a 20 sided dice in the shape of a icosahedron. The number of sides on the dice changes the chance, or probability of rolling a 1. The fewer sides, the more likely you are to roll a 1 and the more sides the less likely. For example, with a 4 sided dice, there’s a one in 4 chance of rolling a 1 and with a 20 sided dice there’s a one in 20 chance. Luckily, Sonic Pi has the handy one_in fn for describing exactly this. Let’s play:

live_loop :different_probabilities do
  sample :drum_snare_hard if one_in(6)
  sleep 0.125
end

Start the live loop above and you’ll hear the familiar random rhythm. However, don’t stop the code running. Instead, change the 6 to a different value such as 2 or 20 and hit the Run button again. Notice that lower numbers mean the snare drum sounds more frequently and higher numbers mean the snare triggers fewer times. You’re making music with probabilities!

Combining Probabilities

Things get really exciting when you combine multiple samples being triggered with different probabilities. For example:

live_loop :multi_beat do
  sample :elec_hi_snare if one_in(6)
  sample :drum_cymbal_closed if one_in(2)
  sample :drum_cymbal_pedal if one_in(3)
  sample :bd_haus if one_in(4)
  sleep 0.125
end

Again, run the code above and then start changing the probabilities to modify the rhythm. Also, try changing the samples to create an entirely new feel. For example try changing :drum_cymbal_closed to :bass_hit_c for extra bass!

Repeatable Rhythms

Next, we can use our old friend use_random_seed to reset the random stream after 8 iterations to create a regular beat. Type the following code to hear a much more regular and repeating rhythm. Once you hear the beat, try changing the seed value from 1000 to another number. Notice how different numbers generate different beats.

live_loop :multi_beat do
  use_random_seed 1000
  8.times do
    sample :elec_hi_snare if one_in(6)
    sample :drum_cymbal_closed if one_in(2)
    sample :drum_cymbal_pedal if one_in(3)
    sample :bd_haus if one_in(4)
    sleep 0.125
  end
end

One thing I tend to do with this kind of structure is to remember which seeds sound good and make a note of them. That way I can easily re-create my rhythms in future practice sessions or performances.

Bringing it all together

Finally, we can throw in some random bass to give it some nice melodic content. Notice that we can also use our newly discovered probabilistic sequencing method on synths just as well as samples. Don’t leave it at that though - tweak the numbers and make your own track with the power of probabilities!

live_loop :multi_beat do
  use_random_seed 2000
  8.times do
    c = rrand(70, 130)
    n = (scale :e1, :minor_pentatonic).take(3).choose
    synth :tb303, note: n, release: 0.1, cutoff: c if rand < 0.9
    sample :elec_hi_snare if one_in(6)
    sample :drum_cymbal_closed if one_in(2)
    sample :drum_cymbal_pedal if one_in(3)
    sample :bd_haus, amp: 1.5 if one_in(4)
    sleep 0.125
  end
end

- Amplitude Modulation

This month we’re going to take a deep dive into one of Sonic Pi’s most powerful and flexible audio FX - the :slicer. By the end of this article you will have learned how to manipulate the overall volume of parts of our live coded sound in powerful new ways. This will allow you to create new rhythmic and timbral structures and broaden your sonic possibilities.

Slice that Amp

So, what does the :slicer FX actually do? One way to think about it is that it’s just like having someone play around with the volume control on your TV or home hi-fi. Let’s take a look but first, listen to the deep growl of the following code which triggers the :prophet synth:

synth :prophet, note: :e1, release: 8, cutoff: 70
synth :prophet, note: :e1 + 4, release: 8, cutoff: 80

Now, let’s pipe it through the :slicer FX:


with_fx :slicer do
  synth :prophet, note: :e1, release: 8, cutoff: 70
  synth :prophet, note: :e1 + 4, release: 8, cutoff: 80
end

Hear how the slicer acts like it’s muting and unmuting the audio with a regular beat. Also, notice how the :slicer affects all the audio generated between the do/end blocks. You can control the speed at which it turns the audio on and off with the phase: opt which is short for phase duration. Its default value is 0.25 which means 4 times a second at the default BPM of 60. Let’s make it faster:

with_fx :slicer, phase: 0.125 do
  synth :prophet, note: :e1, release: 8, cutoff: 70
  synth :prophet, note: :e1 + 4, release: 8, cutoff: 80
end

Now, play with different phase: durations yourself. Try longer and shorter values. See what happens when you choose a really short value. Also, try different synths such as :beep or :dsaw and different notes. Take a look at the following diagram to see how different phase: values change the number of amplitude changes per beat.

Duração

Phase duration is the length of time for one on/off cycle. Therefore smaller values will make the FX switch on and off much faster than larger values. Good values to start playing with are 0.125, 0.25, 0.5 and 1.

Control Waves

By default, the :slicer FX uses a square wave to manipulate the amplitude through time. This is why we hear the amplitude on for a period, then immediately off for a period, then back on again. It turns out that the square wave is just one of 4 different control waves that are supported by :slicer. The others are saw, triangle and (co)sine. Take a look at the diagram below to see what these look like. We can also hear what they sound like. For example, the following code uses (co)sine as the control wave. Hear how the sound doesn’t turn on and off abruptly but instead smoothly fades in and out:

with_fx :slicer, phase: 0.5, wave: 3 do
  synth :dsaw, note: :e3, release: 8, cutoff: 120
  synth :dsaw, note: :e2, release: 8, cutoff: 100
end

Have a play with the different wave forms by changing the wave: opt to 0 for saw, 1 for square, 2 for triangle and 3 for sine. See how different waves sound with different phase: opts too.

Each of these waves can be inverted with the invert_wave: opt which flips it on the y axis. For example, in a single phase the saw wave typically starts high, and slowly goes down before jumping back to the top. With invert_wave: 1 it will start low and slowly go up before jumping back down again. Additionally, the control wave can be started at different points with the phase_offset: opt which should be a value between 0 and 1. By playing around with phase:, wave:, invert_wave: and phase_offset opts you can dramatically change how the amplitude is modified through time.

Duração

Setting your levels

By default, :slicer switches between amplitude values 1 (fully loud) and 0 (silent). This can be changed with the amp_min: and amp_max: opts. You can use this alongside the sine wave setting to create a simple tremolo effect:

with_fx :slicer, amp_min: 0.25, amp_max: 0.75, wave: 3, phase: 0.25 do
  synth :saw, release: 8
end

This is just like grabbing the volume knob on your hi-fi and moving it up and down just a little so the sound ‘wobbles’ in and out.

Probabilities

One of :slicer’s powerful features is its ability to use probability to choose whether or not to turn the slicer on or off. Before the :slicer FX starts a new phase it rolls a dice and based on the result either uses the selected control wave or keeps the amplitude off. Let’s take a listen:

with_fx :slicer, phase: 0.125, probability: 0.6  do
  synth :tb303, note: :e1, cutoff_attack: 8, release: 8
  synth :tb303, note: :e2, cutoff_attack: 4, release: 8
  synth :tb303, note: :e3, cutoff_attack: 2, release: 8
end

Hear how we now have an interesting rhythm of pulses. Try changing the probability: opt to a different value between 0 and 1. Values closer to 0 will have more space between each sound due to the likelihood of the sound being triggered being much lower.

Another thing to notice is that the probability system in the FX is just like the randomisation system accessible via fns such as rand and shuffle. They are both completely deterministic. This means that each time you hit Run you’ll hear exactly the same rhythm of pulses for a given probability. If you would like to change things around you can use the seed: opt to select a different starting seed. This works exactly the same as use_random_seed but only affects that particular FX.

Finally, you can change the ‘resting’ position of the control wave when the probability test fails from 0 to any other position with the prob_pos: opt:

with_fx :slicer, phase: 0.125, probability: 0.6, prob_pos: 1  do
  synth :tb303, note: :e1, cutoff_attack: 8, release: 8
  synth :tb303, note: :e2, cutoff_attack: 4, release: 8
  synth :tb303, note: :e3, cutoff_attack: 2, release: 8
end

Slicing Beats

One really fun thing to do is to use :slicer to chop a drum beat in and out:

with_fx :slicer, phase: 0.125 do
  sample :loop_mika
end

This allows us to take any sample and create new rhythmical possibilites which is a lot of fun. However, one thing to be careful about is to make sure that the tempo of the sample matches the current BPM in Sonic Pi otherwise the slicing will sound totally off. For example, try swapping :loop_mika with the loop_amen sample to hear how bad this can sound when the tempos don’t align.

Changing tempo

As we have already seen, changing the default BPM with use_bpm will make all the sleep times and synth envelope durations grow or shrink to match the beat. The :slicer FX honours this too, as the phase: opt is actually measured in beats not seconds. We can therefore fix the issue with loop_amen above by changing the BPM to match the sample:

use_sample_bpm :loop_amen
with_fx :slicer, phase: 0.125 do
  sample :loop_amen
end

Bringing it all together

Let’s apply all these ideas into a final example that only uses the :slicer FX to create an interesting combination. Go ahead, start changing it and make it into your own piece!

live_loop :dark_mist do
  co = (line 70, 130, steps: 8).tick
  with_fx :slicer, probability: 0.7, prob_pos: 1 do
    synth :prophet, note: :e1, release: 8, cutoff: co
  end
  
  with_fx :slicer, phase: [0.125, 0.25].choose do
    sample :guit_em9, rate: 0.5
  end
  sleep 8
end
live_loop :crashing_waves do
  with_fx :slicer, wave: 0, phase: 0.25 do
    sample :loop_mika, rate: 0.5
  end
  sleep 16
end

- Five Live Coding Techniques

In this month’s Sonic Pi tutorial we’re going to take a look at how you can start treating Sonic Pi like a real instrument. We therefore need to start thinking of code in a completely different way. Live coders think of code in a similar way to how violinists think of their bow. In fact, just like a violinist can apply various bowing techniques to create different sounds (long slow motions vs short fast hits) we will explore five of the basic live coding techniques that Sonic Pi enables. By the end of this article you’ll be able to start practicing for your own live coded performances.

1. Memorise the Shortcuts

The first tip to live coding with Sonic Pi is to start using the shortcuts. For example, instead of wasting valuable time reaching for the mouse, moving it over to the Run button and clicking, you can simply press alt and r at the same time which is much faster and keeps your fingers at the keyboard ready for the next edit. You can find out the shortcuts for the main buttons at the top by hovering the mouse over them. See section 10.2 of the built-in tutorial for the full list of shortcuts.

When performing, one fun thing to do is to add a bit of flair with your arm motion when hitting shortcuts. For example, it’s often good to communicate to the audience when you’re about to make a change - so embellish your movement when hitting alt-r just like a guitarist would do when hitting a big power chord.

2. Manually Layer your Sounds

Now you can trigger code instantly with the keyboard, you can instantly apply this skill for our second technique which is to layer your sounds manually. Instead of ‘composing’ using lots of calls to play, and sample separated by calls to sleep we will have one call to play which we will manually trigger using alt-r. Let’s try it. Type the following code into a fresh buffer:

synth :tb303, note: :e2 - 0, release: 12, cutoff: 90

Now, hit Run and whilst the sound is playing, modify the code in order to drop down four notes by changing it to the following:

synth :tb303, note: :e2 - 4, release: 12, cutoff: 90

Now, hit Run again, to hear both sounds playing at the same time. This is because Sonic Pi’s Run button doesn’t wait for any previous code to finish, but instead starts the code running at the same time. This means you can easily layer lots of sounds manually with minor or major modifications between each trigger. For example, try changing both the note: and the cutoff: opts and then re-trigger.

You can also try this technique with long abstract samples. For example:

sample :ambi_lunar_land, rate: 1

Try starting the sample off, and then progressively halving the rate: opt between hitting Run from 1 to 0.5 to 0.25 to 0.125 and then even try some negative values such as -0.5. Layer the sounds together and see where you can take it. Finally, try adding some FX.

When performing, working with simple lines of code in this way means that an audience new to Sonic Pi has a good chance to follow what you’re doing and relate the code that they can read to the sounds they are hearing.

3. Master Live Loops

When working with more rhythmic music, it can often be hard to manually trigger everything and keep good time. Instead, it is often better to use a live_loop. This provides repetition for your code whilst also giving the ability to edit the code for the next time round the loop. They also will run at the same time as other live_loops which means you can layer them together both with each other and manual code triggers. Take a look at section 9.2 of the built-in tutorial for more information about working with live loops.

When performing, remember to make use of live_loop’s sync: opt to allow you to recover from accidental runtime mistakes which stop the live loop running due to an error. If you already have the sync: opt pointing to another valid live_loop, then you can quickly fix the error and re-run the code to re-start things without missing a beat.

4. Use the Master Mixer

One of Sonic Pi’s best kept secrets is that it has a master mixer through which all sound flows. This mixer has both a low pass filter and a high pass filter built-in, so you can easily perform global modifications to the sound. The master mixer’s functionality can be accessed via the fn set_mixer_control!. For example, whilst some code is running and making sound, enter this into a spare buffer and hit Run:

set_mixer_control! lpf: 50

After you run this code, all existing and new sounds will have a low pass filter applied to them and will therefore sound more muffled. Note that this means that the new mixer values stick until they are changed again. However, if you want, you can always reset the mixer back to its default state with reset_mixer!. Some of the currently supported opts are: pre_amp:, lpf: hpf:, and amp:. For the full list, see the built-in docs for set_mixer_control!.

Use the mixer’s *_slide opts to slide one or many opts values over time. For example, to slowly slide the mixer’s low pass filter down from the current value to 30, use the following:

set_mixer_control! lpf_slide: 16, lpf: 30

You can then slide quickly back to a high value with:

set_mixer_control! lpf_slide: 1, lpf: 130

When performing, it’s often useful to keep a buffer free for working with the mixer like this.

5. Practice

The most important technique for live coding is practice. The most common attribute across professional musicians of all kinds is that they practice playing with their instruments - often for many hours a day. Practice is just as important for a live coder as a guitarist. Practice allows your fingers to memorise certain patterns and common edits so you can type and work with them more fluently. Practice also gives you opportunities to explore new sounds and code constructs.

When performing, you’ll find the more practice you do, the easier it will be for you to relax into the gig. Practice will also give you a wealth of experience to draw from. This can help you understand which kinds of modifications will be interesting and also work well with the current sounds.

Bringing it all together

This month, instead of giving you a final example that combines all the things discussed, let’s part by setting down a challenge. See if you can spend a week practicing one of these ideas every day. For example, one day practice manual triggers, the next do some basic live_loop work and the following day play around with the master mixer. Then repeat. Don’t worry if things feel slow and clunky at first - just keep practicing and before you know it you’ll be live coding for a real audience.


- 8 Tips for Live Coding Practice

Last month we took a look at five important techniques for mastering live coding - in other words, we explored how we could use Sonic Pi to approach code in the same way we would approach a musical instrument. One of the important concepts that we discussed was practice. This month we’re going to take a deeper dive into understanding why live coding practice is important and how you might start.

Practice regularly

The most important piece of advice is to make sure you practice regularly. As a rule I typically practice for 1-2 hours a day, but 20 mins is just fine when you’re starting out. Little but often is what you’re aiming for - so if you can only manage 10 minutes, that’s a great start.

Practice tip #1 - start to develop a practice routine. Find a nice time in the day that works for you and try and practice at that time as many days of the week as you can. Before long you’ll be looking forward to your regular session.

Learn to Touch Type

If you watch a professional musician performing on stage you’ll likely notice a few things. Firstly, when they play they don’t stare at their instrument. Their fingers, arms and bodies know which keys to press, strings to pluck or drums to hit without them having to think about it too much. This is known as “muscle memory” and although it might sound like something only professionals can do - it’s just the same as when you first learned to walk or ride a bike - practicing through repetition. Live coders use muscle memory to free their minds from having to think about where to move their fingers so they can focus on the music. This is called touch-typing - typing without having to look at the keyboard.

Practice tip #2 - learn how to touch type. There are many apps, websites and even games which can help you achieve this. Find one you like the look of and stick at it until you can code without looking down.

Code whilst standing

The body of a musician is conditioned for playing their instrument. For example, a trumpet player needs to be able to blow hard, a guitar player needs to be able to grip the fretboard with strength and a drummer needs to be able to continually hit the drums for long periods of time. So, what’s physical about live coding? Just like DJs, live coders typically perform whilst standing up and some even dance whilst they code! If you practice live coding whilst sitting at a desk and then have to get up and stand at a gig, you’ll likely find the difference very difficult and frustrating.

Practice tip #3 - stand whilst you practice. The easiest way to do this is to use a standing height desk. However, if like me you don’t have one at home, there’s a couple of low-fi options. The approach I take is to use an ironing board which happens to work rather well. Another is to stack some boxes or large books on a normal desk and place your keyboard on top of that. Also, make sure you stretch before you start practicing and try and dance a little during the session. Remember, nobody is watching you, so have fun and you’ll feel much more natural on stage.

Practice setting up

Most instruments require some assembly and tuning before they can be played. Unless you’re a rockstar with a bus full of roadies, you’ll have to set up your own instrument before your gig. This is often a stressful time and it is easy for problems to occur. One way to help with this is to incorporate the setup process into your practice sessions.

Practice tip #4 - treat setting up as an important part of your practice. For example, have a box or bag that you can keep your Raspberry Pi and keyboard in etc. Before each practice session, take out all the parts, connect everything, and work through the boot process until you have Sonic Pi running and can make sounds. Once you’ve finished practicing, take the time to carefully pack everything away afterwards. This may take some time at first, but before long you’ll be able to setup and pack everything away incredibly quickly without having to think about it.

Experiment Musically

Once you’ve set up and are ready to start making music, you might find yourself struggling to know where to start. One problem many people face is that they might have a good idea of the kinds of sounds they want to make, but are frustrated that they can’t produce them. Some people don’t even know what kind of sounds they want to make! The first thing to do is not to worry - this is very common and happens to every musician - even if they’ve been practicing for a long time. It is much more important to be making sounds you don’t like than not making any sounds at all.

Practice tip #5 - spend time making sounds and music you don’t like. Try to make time to explore new sounds and ideas. Don’t worry that it might sound terrible if it’s not the style you’re looking for. When you’re experimenting like this you increase the chance of stumbling over a sound or combination of sounds which you love! Even if 99% of the sounds you make are bad, that 1% might be the riff or intro to your new track. Forget the things you don’t like and remember the parts you do. This is even easier when you’re making music with code - just hit save!

Hear the Code

rand

Practice tip #6 - write some code into Sonic Pi but don’t hit the Run button. Instead, try to imagine what sound it is going to produce. Then, hit Run, listen, and think about what you got right and what you didn’t. Keep repeating this until it become a natural part of your coding process. When I practice I normally have a good idea of what the code will sound like. However, I still am occasionally surprised, and then I’ll stop and spend some time thinking about why I was wrong. Each time this happens, I learn new tricks which allow me to express myself in new ways.

Remove all distractions

A common problem when practicing is to become distracted with other things. Practicing is hard and requires real discipline regardless of the kind of music you’re making - from jazz to classical to EDM. If you’re struggling to get started or make progress, it’s often too easy to hop on social media, or look something up on the internet etc. If you’ve set yourself a target of 20 minutes of practice, it’s important to try and spend all that time being as productive as possible.

Practice tip #7 - before you start practicing remove as many distractions as possible. For example, disconnect from the internet, put your phone in another room and try to practice in a quiet place where you’re unlikely to be disturbed. Try to focus on coding music and you can return to your distractions when you’ve finished.

Keep a practice diary

When you are practicing, you’ll often find your mind is full of new exciting ideas - new musical directions, new sounds to try out, new functions to write, etc. These ideas are often so interesting that you might stop what you’re doing and start working on the idea. This is another form of distraction!

Practice tip #8 - keep a practice diary by your keyboard. When you get an exciting new idea, temporarily pause your practice session, quickly jot the idea down, then forget about it and carry on practicing. You can then spend some quality time thinking about and working on your ideas after you’ve finished practicing.

Bringing it all together

Try to establish a practice routine which incorporates as many of these ideas as possible. Try to keep the sessions as fun as possible but be aware that some practice sessions will be hard and feel a little like work. However, it will all be worth it once you’ve created your first piece or given your first performance. Remember, practice is the key to success!


- Sample Stretching

When people discover Sonic Pi, one of the first things they learn is how simple it is to play pre-recorded sounds using the sample function. For example, you can play an industrial drum loop, hear the sound of a choir or even listen to a vinyl scratch all via a single line of code. However, many people don’t realise that you can actually vary the speed that the sample is played back at for some powerful effects and a whole new level of control over your recorded sounds. So, fire up a copy of Sonic Pi and let’s get started stretching some samples!

Slowing Samples Down

To modify the playback rate of a sample we need to use the rate: opt:

sample :guit_em9, rate: 1

If we specify a rate: of 1 then the sample is played back at the normal rate. If we want to play it back at half speed we simply use a rate: of 0.5:

sample :guit_em9, rate: 0.5

Notice that this has two effects on the audio. Firstly the sample sounds lower in pitch and secondly it takes twice as long to play back (see the sidebar for an explanation of why this is the case). We can even choose lower and lower rates moving towards 0, so a rate: of 0.25 is a quarter speed, 0.1 is a tenth of the speed, etc. Try playing with some low rates and see if you can turn the sound into a low rumble.

Speeding Samples Up

In addition to making the sound longer and lower using a small rate, we can use higher rates to make the sound shorter and higher. Let’s play with a drum loop this time. First, take a listen to how it sounds at the default rate of 1:

sample :loop_amen, rate: 1

Now, let’s speed it up a little:

sample :loop_amen, rate: 1.5

Ha! We just moved musical genres from old-skool techno to jungle. Notice how the pitch of each drum hit is higher as well as how the whole rhythm speeds up. Now, try even higher rates and see how high and short you can make the drum loop. For example, if you use a rate of 100, the drum loop turns into a click!

Reverse Gear

Now, I’m sure many of you are thinking the same thing right now… “what if you use a negative number for the rate?”. Great question! Let’s think about this for a moment. If our rate: opt signifies the speed with which the sample is played back, 1 being normal speed, 2 being double speed, 0.5 being half speed, -1 must mean backwards! Let’s try it on a snare. First, play it back at the normal rate:

sample :elec_filt_snare, rate: 1

Now, play it backwards:

sample :elec_filt_snare, rate: -1

Of course, you can play it backwards twice as fast with a rate of -2 or backwards at half speed with a rate of -0.5. Now, play around with different negative rates and have fun. It’s particularly amusing with the :misc_burp sample!

Sample, Rate and Pitch

One of the effects of rate modification on samples is that faster rates result in the sample sounding higher in pitch and slower rates result in the sample sounding lower in pitch. Another place you may have heard this effect in every day life is when you’re cycling or driving past a beeping pedestrian crossing - as you’re heading towards the sound source the pitch is higher than when you’re moving away from the sound - the so-called Doppler effect. Why is this?

Let’s consider a simple beep which is represented by a sine wave. If we use an oscilloscope to plot a beep, we’ll see something like Figure A. If we plot a beep an octave higher, we’ll see Figure B and an octave lower will look like Figure C. Notice that the waves of higher notes are more compact and the waves of lower notes are more spread out.

A sample of a beep is nothing more than a lot of numbers (x, y, coordinates) which when plotted onto a graph will re-draw the original curves. See figure D where each circle represents a coordinate. To turn the coordinates back into audio, the computer works through each x value and sends the corresponding y value to the speakers. The trick here is that the rate at which the computer works through the x numbers does not have to be the same as the rate with which they were recorded. In other words, the space (representing an amount of time) between each circle can be stretched or compressed. So, if the computer walks through the x values faster than the original rate, it will have the effect of squashing the circles closer together which will result in a higher sounding beep. It will also make the beep shorter as we will work through all the circles faster. This is shown in Figure E.

Finally, one last thing to know is that a mathematician called Fourier proved that any sound is actually lots and lots of sine waves all combined together. Therefore, when we compress and stretch any recorded sound we’re actually stretching and compressing many sine waves all at the same time in exactly this manner.

Pitch Bending

As we’ve seen, using a faster rate will make the sound higher in pitch and a slower rate will make the sound lower in pitch. A very simple and useful trick is to know that doubling the rate actually results in the pitch being an octave higher and inversely halving the rate results in the pitch being an octave lower. This means that for melodic samples, playing it alongside itself at double/half rates actually sounds rather nice:

sample :bass_trance_c, rate: 1
sample :bass_trance_c, rate: 2
sample :bass_trance_c, rate: 0.5

However, what if we just want to alter the rate such that the pitch goes up one semitone (one note up on a piano)? Sonic Pi makes this very easy via the rpitch: opt:

sample :bass_trance_c
sample :bass_trance_c, rpitch: 3
sample :bass_trance_c, rpitch: 7

If you take a look at the log on the right, you’ll notice that an rpitch: of 3 actually corresponds to a rate of 1.1892 and a rpitch: of 7 corresponds to a rate of 1.4983. Finally, we can even combine rate: and rpitch: opts:

sample :ambi_choir, rate: 0.25, rpitch: 3
sleep 3
sample :ambi_choir, rate: 0.25, rpitch: 5
sleep 2
sample :ambi_choir, rate: 0.25, rpitch: 6
sleep 1
sample :ambi_choir, rate: 0.25, rpitch: 1

Bringing it all together

Let’s take a look at a simple piece which combines these ideas. Copy it into an empty Sonic Pi buffer, hit play, listen to it for a while and then use it as a starting point for your own piece. See how much fun it is to manipulate the playback rate of samples. As an added exercise try recording your own sounds and play around with the rate to see what crazy sounds you can make.

live_loop :beats do
  sample :guit_em9, rate: [0.25, 0.5, -1].choose, amp: 2
  sample :loop_garzul, rate: [0.5, 1].choose
  sleep 8
end
 
live_loop :melody do
  oct = [-1, 1, 2].choose * 12
  with_fx :reverb, amp: 2 do
    16.times do
      n = (scale 0, :minor_pentatonic).choose
      sample :bass_voxy_hit_c, rpitch: n + 4 + oct
      sleep 0.125
    end
  end
end

- Additive Synthesis

This is the first of a short series of articles on how to use Sonic Pi for sound design. We’ll be taking a quick tour of a number of different techniques available for you to craft your own unique sound. The first technique we’ll look at is called additive synthesis. This may sound complicated - but if we expand each word slightly the meaning pops right out. Firstly, additive means a combination of things and secondly synthesis means to create sound. Additive synthesis therefore means nothing more complicated than combining existing sounds to create new ones. This synthesis technique dates back a very long time - for example, pipe organs in the middle ages had lots of slightly different sounding pipes which you could enable or disable with stops. Pulling out the stop for a given pipe ‘added it to the mix’ making the sound richer and more complex. Now, let’s see how we can pull out all the stops with Sonic Pi.

Simple Combinations

Let’s start with the most basic sound there is - the humble pure-toned sine wave:

synth :sine, note: :d3

Now, let’s see how this sounds combined with a square wave:

synth :sine, note: :d3
synth :square, note: :d3

Notice how the two sounds combine to form a new, richer sound. Of course, we don’t have to stop there, we can add as many sounds as we need. However, we need to be careful with how many sounds we add together. Just like when we mix paints to create new colours, adding too many colours will result in a messy brown, similarly - adding too many sounds together will result in a muddy sound.

Blending

Let’s add something to make it sound a little brighter. We could use a triangle wave at an octave higher (for that high bright sound) yet only play it at amp 0.4 so it adds something extra to the sound rather than taking it over:

synth :sine, note: :d3
synth :square, note: :d3
synth :tri, note: :d4, amp: 0.4

Now, try creating your own sounds by combining 2 or more synths at different octaves and amplitudes. Also, note that you can play around with each synth’s opts to modify each source sound before it is mixed in for even more combinations of sounds.

Detuning

So far, when combining our different synths we’ve used either the same pitch or switched octave. How might it sound if we didn’t stick to octaves but instead chose a slightly higher or lower note? Let’s try it:

detune = 0.7
synth :square, note: :e3
synth :square, note: :e3 + detune

If we detune our square waves by 0.7 notes we hear something that perhaps doesn’t sound in tune or correct - a ‘bad’ note. However, as we move closer to 0 it will sound less and less out of tune as the pitches of the two waves get closer and more similar. Try it for yourself! Change the detune: opt value from 0.7 to 0.5 and listen to the new sound. Try 0.2, 0.1, 0.05, 0. Each time you change the value, take a listen and see if you can hear how the sound is changing. Notice that low detune values such as 0.1 produce a really nice ‘thick’ sound, with both slightly different pitches interacting with each other in interesting, often surprising, ways.

:dsaw

Amplitude shaping

Another way we can finely craft our sound is to use a different envelope and options for each synth trigger. For example this will allow you to make some aspects of the sound percussive and other aspects ring out for a period of time.

detune = 0.1
synth :square, note: :e1, release: 2
synth :square, note: :e1 + detune, amp: 2, release: 2
synth :gnoise, release: 2, amp: 1, cutoff: 60
synth :gnoise, release: 0.5, amp: 1, cutoff: 100
synth :noise, release: 0.2, amp: 1, cutoff: 90

In the example above I have mixed in a noisy percussive element to the sound along with some more persistent background rumbling. This was achieved firstly by using two noise synths with middling cutoff values (90 and 100) using short release times along with a noise with a longer release time but with a low cutoff value (which makes the noise less crisp and more rumbly.)

Bringing it all together

Let’s combine all these techniques to see if we can use additive synthesis to re-create a basic bell sound. I’ve broken this example into four sections. Firstly we have the ‘hit’ section which is the initial onset part of the bell sound - so uses a short envelope (e.g. a release: of around 0.1). Next we have the long ringing section in which I’m using the pure sound of the sine wave. Notice that I’m often increasing the note by roughly 12 and 24 which are the number of notes in one and two octaves. I have also thrown in a couple of low sine waves to give the sound some bass and depth. Finally, I used define to wrap my code in a function which I can then use to play a melody. Try playing your own melody and also messing around with the contents of the :bell function until you create your own crazy sound to play with!

define :bell do |n|
  # Triangle waves for the 'hit'
  synth :tri, note: n - 12, release: 0.1
  synth :tri, note: n + 0.1, release: 0.1
  synth :tri, note: n - 0.1, release: 0.1
  synth :tri, note: n, release: 0.2
  # Sine waves for the 'ringing'
  synth :sine, note: n + 24, release: 2
  synth :sine, note: n + 24.1, release: 2
  synth :sine, note: n + 24.2, release: 0.5
  synth :sine, note: n + 11.8, release: 2
  synth :sine, note: n, release: 2
  # Low sine waves for the bass
  synth :sine, note: n - 11.8, release: 2
  synth :sine, note: n - 12, release: 2
end
# Play a melody with our new bell!
bell :e3
sleep 1
bell :c2
sleep 1
bell :d3
sleep 1
bell :g2

- Subtractive Synthesis

This is the second in a series of articles on how to use Sonic Pi for sound design. Last month we looked at additive synthesis which we discovered was the simple act of playing multiple sounds at the same time to make a new combined sound. For example we could combine different sounding synths or even the same synth at different pitches to build a new complex sound from simple ingredients. This month we’ll look at a new technique commonly called subtractive synthesis which is simply the act of taking an existing complex sound and removing parts of it to create something new. This is a technique which is commonly associated with the sound of analog synthesisers of the 1960s and 1970s but also with the recent renaissance of modular analog synths through popular standards such as Eurorack.

Despite this sounding like a particularly complicated and advanced technique, Sonic Pi makes it surprisingly simple and easy - so let’s dive right in.

Complex Source Signal

For a sound to work well with subtractive synthesis, it typically needs to be fairly rich and interesting. This doesn’t mean we need something hugely complex - in fact, just a standard :square or :saw wave will do:

synth :saw, note: :e2, release: 4

Notice that this sound is already pretty interesting and contains many different frequencies above :e2 (the second E on a piano) which add to create the timbre. If that didn’t make much sense to you, try comparing it with the :beep:

synth :beep, note: :e2, release: 4

As the :beep synth is just a sine wave, you’ll hear a much purer tone and only at :e2 and none of the high crispy/buzzy sounds which you heard in the :saw. It’s this buzziness and variation from a pure sine wave that we can play with when we use subtractive synthesis.

Filters

Once we have our raw source signal, the next step is to pass it through a filter of some kind which will modify the sound by removing or reducing parts of it. One of the most common filters used for subtractive synthesis is something called a low pass filter. This will allow all the low parts of the sound through but will reduce or remove the higher parts. Sonic Pi has a powerful yet simple to use FX system that includes a low pass filter, called :lpf. Let’s play with it:

with_fx :lpf, cutoff: 100 do
  synth :saw, note: :e2, release: 4
end

If you listen carefully you’ll hear how some of that buzziness and crispiness has been removed. In fact, all the frequencies in the sound above note 100 have been reduced or removed and only the ones below are still present in the sound. Try changing that cutoff: point to lower notes, say 70 and then 50 and compare the sounds.

Of course, the :lpf isn’t the only filter you can use to manipulate the source signal. Another important FX is the high pass filter referred to as :hpf in Sonic Pi. This does the opposite to :lpf in that it lets the high parts of the sound through and cuts off the low parts.

with_fx :hpf, cutoff: 90 do
  synth :saw, note: :e2, release: 4
end

Notice how this sounds much more buzzy and raspy now that all the low frequency sounds have been removed. Play around with the cutoff value - notice how lower values let more of the original bass parts of the source signal through and higher values sound increasingly tinny and quiet.

Low Pass Filter

The low pass filter is such an important part of every subtractive synthesis toolkit that it’s worth taking a deeper look at how it works. This diagram shows the same sound wave (the :prophet synth) with varying amounts of filtering. At the top, section A shows the audio wave with no filtering. Notice how the wave form is very pointy and contains lots of sharp edges. It is these hard, sharp angles that produce the high crispy/buzzy parts of the sound. Section B shows the low pass filter in action - notice how it is less pointy and more rounded than the wave form above. This means that the sound will have fewer high frequencies giving it a more mellow rounded feel. Section C shows the low pass filter with a fairly low cutoff value - this means that even more of the high frequencies have been removed from the signal resulting in an even softer, rounder wave form. Finally, notice how the size of the wave form, which represents the amplitude, decreases as we move from A to C. Subtractive synthesis works by removing parts of the signal which means that the overall amplitude is reduced as the amount of filtering that is taking place increases.

Filter Modulation

So far we’ve just produced fairly static sounds. In other words, the sound doesn’t change in any way for the entirety of its duration. Often you might want some movement in the sound to give the timbre some life. One way to achieve this is via filter modulation - changing the filter’s options through time. Luckily Sonic Pi gives you powerful tools to manipulate an FX’s opts through time. For example, you can set a slide time to each modulatable opt to specify how long it should take for the current value to linearly slide to the target value:

with_fx :lpf, cutoff: 50 do |fx|
  control fx, cutoff_slide: 3, cutoff: 130
  synth :prophet, note: :e2, sustain: 3.5
end

Let’s take a quick look at what’s going on here. Firstly we start an :lpf FX block as normal with an initial cutoff: of a very low 20. However, the first line also finishes with the strange |fx| at the end. This is an optional part of the with_fx syntax which allows you to directly name and control the running FX synth. Line 2 does exactly this and controls the FX to set the cutoff_slide: opt to 4 and the new target cutoff: to be 130. The FX will now start sliding the cutoff: opt’s value from 50 to 130 over a period of 3 beats. Finally we also trigger a source signal synth so we can hear the effect of the modulated low pass filter.

Bringing it all together

This is just a very basic taster of what’s possible when you use filters to modify and change a source sound. Try playing with Sonic Pi’s many built-in FX to see what crazy sounds you can design. If your sound feels too static, remember you can start modulating the options to create some movement.

Let’s finish by designing a function which will play a new sound created with subtractive synthesis. See if you can figure out what’s going on here - and for the advanced Sonic Pi readers out there - see if you can work out why I wrapped everything inside a call to at (please send answers to @samaaron on Twitter).

define :subt_synth do |note, sus|
  at do
    with_fx :lpf, cutoff: 40, amp: 2 do |fx|
      control fx, cutoff_slide: 6, cutoff: 100
      synth :prophet, note: note, sustain: sus
    end
    with_fx :hpf, cutoff_slide: 0.01 do |fx|
      synth :dsaw, note: note + 12, sustain: sus
      (sus * 8).times do
        control fx, cutoff: rrand(70, 110)
        sleep 0.125
      end
    end
  end
end
subt_synth :e1, 8
sleep 8
subt_synth :e1 - 4, 8

- Creative coding in the classroom with Sonic Pi

(This article was published in issue 9 of the Hello World Magazine)

Code is one of the most creative media that humans have created. The initially obscure symbols of parentheses and lambdas are not just deeply rooted in science and mathematics, they are the closest we have managed to get to casting the same kind of magical spells as Gandalf and Harry Potter. I believe that this provides a powerful means of engagement in our learning spaces. Through the magic of code we are able to conjure up individually meaningful stories and learning experiences.

We are surrounded by magical experiences. From the sleight of hand of a stage magician making the ball disappear into thin air, to the wonder of seeing your favourite band perform on a big stage. It is these “wow” moments that inspire us to pick up a magic book and learn the French Drop or to start jamming power chords on an old guitar. How might we create similarly deep and lasting senses of wonder that will motivate people to practice and learn the fundamentals of programming?

Musical Engines and Notation

The histories of music and computers have been intricately woven together since the inception of computing machines, or “engines” as Charles Babbage’s powerful analytical engine was called. Back in 1842 the Mathematician Ada Lovelace, who worked very closely with Babbage, saw the creative potential of these engines. Whilst these first engines had originally been designed to accurately solve hard maths problems, Ada dreamt about making music with them:

”..the engine might compose elaborate and scientific pieces of music of any degree of complexity or extent.” Ada Lovelace, 1842.

Of course, today in 2019 much of our music, regardless of genre, has either been composed, produced or mastered with a digital computer. Ada’s dream came true. It is even possible to trace the history back even further. If you see coding as the art of writing sequences of special symbols that instruct a computer to do specific things, then musical composition is a very similar practice. In Western music, the symbols are black dots positioned on a stave of lines that tell the musician which notes to play and when. Intriguingly, if we trace the roots of Western music notation back to the Italian Benedictine monk, Guido d’Arezzo, we find that the dots and lines system that modern orchestras use is just one of a number of notation systems he worked on. Some of the others were much closer to what we might now see as code.

In education, magical meaningful experiences with computers and programming languages have been explored since the late ’60s. Computer education pioneers Seymour Papert, Marvin Minsky and Cynthia Solomon explored simple Lisp-based languages that moved pens over large pieces of paper. With just a few simple commands it was possible to program the computer to draw any picture. They even experimented by extending their Logo language from drawing to music. Papert wrote about learning through experiencing the reconstruction of knowledge rather than its transmission. Getting people to play with things directly was an important part of his group’s work.

Sonic Pi Performances

Programação ao vivo

Sonic Pi has been used to perform in a wide range of venues such as school halls, nightclubs, outdoor stages at musical festivals, college chapels and prestigious music venues. For example the amazing Convo project which brought 1000 children together in the Royal Albert Hall to perform an ambitious new composition by composer Charlotte Harding. The piece was written for traditional instruments, choirs, percussion and Sonic Pi code. The pop-artist Jylda also performed with Sonic Pi in the Sage Gateshead for the Thinking Digital Conference, where she created a unique live-coded improvised remix of her song Reeled.

Sonic Pi in the Royal Albert Hall Sonic Pi used as one of the instruments as part of Convo at the Royal Albert Hall. Photo credit: Pete Jones.

Live coding in the classroom

Sonic Pi is a code-based music creation and performance tool that builds on all of these ideas. Unlike the majority of computing education software, it is both simple enough to use for education and also powerful enough for professionals. It has been used to perform in international music festivals, used to compose in a range of styles from classical, EDM and heavy metal, and was even reviewed in the Rolling Stone magazine. It has a diverse community of over 1.5 million live coders with a variety of backgrounds all learning and sharing their ideas and thoughts through the medium of code. It is free to download for Mac, PC and Raspberry Pi and includes a friendly tutorial that assumes you know nothing about either code or music.

Sonic Pi was initially conceived as a response to the UK’s newly released Computing curriculum in 2014. The goal was to find a motivating and fun way to teach the fundamentals of programming. It turns out that there is a lot in common and it’s huge fun to explain sequencing as melody, iteration as rhythm, conditionals as musical variety. I developed the initial designs and first iterations of the platform with Carrie Anne Philbin, who brought a teacher’s perspective to the project. Since then, Sonic Pi has undergone iterative improvements thanks to the feedback gained from observing learners and collaborating directly with educators in the classroom. A core design philosophy was to never add a feature that couldn’t be easily taught to a 10 year old child. This meant that most ideas had to be heavily refined and reworked until they were simple enough. Making things simple whilst keeping them powerful continues to be the hardest part of the project.

In order to provide the magical motivation, Sonic Pi’s design was never limited to a pure focus on education. Ideally there would be famous musicians and performers using Sonic Pi as a standard instrument alongside guitars, drums, vocals, synths, violins, etc. These performers would then act as motivational role models demonstrating the creative potential of code. For this to be possible sufficient focus and effort therefore had to be placed on making it a powerful instrument whilst still keeping it simple enough for 10 year olds to pick up. In addition to educators, I also worked directly with a variety of different artists in classrooms, art galleries, studios and venues in the early stages of Sonic Pi’s development. This provided essential feedback which enabled Sonic Pi to grow and ultimately flourish as a tool for creative expression.

There were a number of exciting and unexpected side effects of this dual focus on education and professional musicians. Many of the features are beneficial to both groups. For example, a lot of effort has been put into making error messages more friendly and useful (rather than being a huge complicated mess of jargon). This turns out to be very useful when you write a bug while performing in front of thousands of people. Additionally, functionality such as playing studio quality audio samples, adding audio effects, providing access to live audio from the microphone all turn out to make the learning experience more fun, rewarding and ultimately meaningful.

The Sonic Pi community continues to grow and share amazing code compositions, lesson plans, musical algorithms, and much more. Much of this happens on our friendly forum in_thread (in-thread.sonic-pi.net) which is home to a very diverse group of people that includes educators, musicians, programmers, artists and makers. It is a real joy to see people learn to use code to express themselves in new ways and for that in turn to inspire others to do the same.

- Some fun capabilities

From a Computer Science perspective, Sonic Pi provides you with the building blocks to teach you the basics as found in the UK’s curriculum such as sequencing, iteration, conditionals, functions, data structures, algorithms, etc. However, it also builds on a number of important and relevant concepts which have become adopted in mainstream industry such as concurrency, events, pattern matching, distributed computing and determinism - all whilst keeping things simple enough to explain to a 10 year old child.

Getting started is as simple as:

play 70

A melody can be constructed with one more command, sleep:

play 72
sleep 0.5
play 75
sleep 0.5
play 79

In this example, we play the note 70 (roughly the 70th note on a piano), wait for 1 second, play note 72, wait for half a second and then play note 75. What’s interesting here is that with just two commands we have access to pretty much all of Western notation (which notes to play and when) and learners can code any melody they’ve ever heard. This leads to huge variety in expressive outcomes whilst focussing on the same computing concept: sequencing in this case.

Taking ideas from the professional music world, we can also play back any recorded sound. Sonic Pi can play any audio file on your computer but also has a number of sounds built-in to make things easy to get started:

sample :loop_amen

This code will play back the drum break which was a pillarstone to early hip-hop, Drum and Bass and Jungle. For example, a number of early hip-hop artists played this drum break back at half speed to give it a more laid-back feeling:

sample :loop_amen, rate: 0.5

In the 90s a number of music scenes burst out of new technology which enabled artists to take drum breaks like this apart and reassemble in a different order. For example:

sample :loop_amen

rand


- Essential Knowledge

This section will cover some very useful - in fact essential - knowledge for getting the most out of your Sonic Pi experience.

We’ll cover how to take advantage of the many keyboard shortcuts available to you, how to share your work and some tips on performing with Sonic Pi.


- Using Shortcuts

Sonic Pi is as much an instrument as a coding environment. Shortcuts can therefore make playing Sonic Pi much more efficient and natural - especially when you’re playing live in front of an audience.

Much of Sonic Pi can be controlled through the keyboard. As you gain more familiarity working and performing with Sonic Pi, you’ll likely start using the shortcuts more and more. I personally touch-type (I recommend you consider learning too) and find myself frustrated whenever I need to reach for the mouse as it slows me down. I therefore use all of these shortcuts on a very regular basis!

Therefore, if you learn the shortcuts, you’ll learn to use your keyboard effectively and you’ll be live coding like a pro in no time.

However, don’t try and learn them all at once, just try and remember the ones you use most and then keep adding more to your practice.

Consistency across Platforms

Imagine you’re learning the clarinet. You’d expect all clarinets of all makes to have similar controls and fingerings. If they didn’t, you’d have a tough time switching between different clarinets and you’d be stuck to using just one make.

Unfortunately the three major operating systems (Linux, Mac OS X and Windows) come with their own standard defaults for actions such as cut and paste etc. Sonic Pi will try and honour these standards. However priority is placed on consistency across platforms within Sonic Pi rather than attempting to conform to a given platform’s standards. This means that when you learn the shortcuts whilst playing with Sonic Pi on your Raspberry Pi, you can move to the Mac or PC and feel right at home.

Control and Meta

Part of the notion of consistency is the naming of shortcuts. In Sonic Pi we use the names Control and Meta to refer to the two main combination keys. On all platforms Control is the same. However, on Linux and Windows, Meta is actually the Alt key while on Mac Meta is the Command key. For consistency we’ll use the term Meta - just remember to map that to the appropriate key on your operating system.

Abbreviations

To help keep things simple and readable, we’ll use the abbreviations C- for Control plus another key and M- for Meta plus another key. For example, if a shortcut requires you to hold down both Meta and r we’ll write that as M-r. The - just means “at the same time as.”

The following are some of the shortcuts I find most useful.

Stopping and starting

Instead of always reaching for the mouse to run your code, you can simply press M-r. Similarly, to stop running code you can press M-s.

I’m really lost without the navigation shortcuts. I therefore highly recommend you spend the time to learn them. These shortcuts also work extremely well when you’ve learned to touch type as they use the standard letters rather than requiring you to move your hand to the mouse or the arrow keys on your keyboard.

You can move to the beginning of the line with C-a, the end of the line with C-e, up a line with C-p, down a line with C-n, forward a character with C-f, and back a character with C-b. You can even delete all the characters from the cursor to the end of the line with C-k.

Tidy Code

To auto-align your code simply press M-m.

Help System

To toggle the help system you can press M-i. However, a much more useful shortcut to know is C-i which will look up the word underneath the cursor and display the docs if it finds anything. Instant help!

For a full list take a look at section 10.2 Shortcut Cheatsheet.


- Shortcut Cheatsheet

The following is a summary of the main shortcuts available within Sonic Pi. Please see Section 10.1 for motivation and background.

Conventions

In this list, we use the following conventions (where Meta is one of Alt on Windows/Linux or Cmd on Mac):

C-a means hold the Control key then press the a key whilst holding them both at the same time, then releasing. M-r means hold the Meta key and then press the r key whilst holding them both at the same time, then releasing. S-M-z means hold the Shift key, then the Meta key, then finally the z key all at the same time, then releasing. C-M-f means hold the Control key, then press Meta key, finally the f key all at the same time, then releasing.

Main Application Manipulation

M-r - Run code M-s - Stop code M-i - Toggle Help System M-p - Toggle Preferences M-{ - Switch buffer to the left M-} - Switch buffer to the right M-+ - Increase text size of current buffer M-- - Decrease text size of current buffer

Selection/Copy/Paste

M-a - Select all M-c - Copy selection to paste buffer M-] - Copy selection to paste buffer M-x - Cut selection to paste buffer C-] - Cut selection to paste buffer C-k - Cut to the end of the line M-v - Paste from paste buffer to editor C-y - Paste from paste buffer to editor C-SPACE - Set mark. Navigation will now manipulate highlighted region. Use C-g to escape.

Text Manipulation

M-m - Align all text Tab - Align current line or selection (or select autocompletion) C-l - Centre editor M-/ - Comment/Uncomment current line or selection C-t - Transpose/swap characters M-u - Convert next word (or selection) to upper case. M-l - Convert next word (or selection) to lower case.

C-a - Move to beginning of line C-e - Move to end of line C-p - Move to previous line C-n - Move to next line C-f - Move forward one character C-b - Move backward one character M-f - Move forward one word M-b - Move backward one word C-M-n - Move line or selection down C-M-p - Move line or selection up S-M-u - Move up 10 lines S-M-d - Move down 10 lines M-< - Move to beginning of buffer M-> - Move to end of buffer

Deletion

C-h - Delete previous character C-d - Delete next character

Advanced Editor Features

C-i - Show docs for word under cursor M-z - Undo S-M-z - Redo C-g - Escape S-M-f - Toggle fullscreen mode S-M-b - Toggle visibility of buttons S-M-l - Toggle visibility of log S-M-m - Toggle between light/dark modes S-M-s - Save contents of buffer to a file S-M-o - Load contents of buffer from a file


- Sharing

Sonic Pi is all about sharing and learning with each other.

Once you’ve learned how to code music, sharing your compositions is as simple as sending an email containing your code. Please do share your code with others so they can learn from your work and even use parts in a new mash-up.

If you’re unsure of the best way to share your work with others I recommend putting your code on GitHub and your music on SoundCloud. That way you’ll be able to easily reach a large audience.

Code -> GitHub

GitHub is a site for sharing and working with code. It’s used by professional developers as well as artists for sharing and collaborating with code. The simplest way to share a new piece of code (or even an unfinished piece) is to create a Gist. A Gist is a simple way of uploading your code in a simple way that others can see, copy and share.

Audio -> SoundCloud

Another important way of sharing your work is to record the audio and upload it to SoundCloud. Once you’ve uploaded your piece, other users can comment and discuss your work. I also recommend placing a link to a Gist of your code in the track description.

To record your work, hit the Rec button in the toolbar, and recording starts immediately. Hit Run to start your code if it isn’t already in progress. When you’re done recording, press the flashing Rec button again, and you’ll be prompted to enter a filename. The recording will be saved as a WAV file, which can be edited and converted to MP3 by any number of free programs (try Audacity for instance).

Hope

I encourage you to share your work and really hope that we’ll all teach each other new tricks and moves with Sonic Pi. I’m really excited by what you’ll have to show me.


- Performing

One of the most exciting aspects of Sonic Pi is that it enables you to use code as a musical instrument. This means that writing code live can now be seen as a new way of performing music.

We call this Live Coding.

Show Your Screen

When you live code I recommend you show your screen to your audience. Otherwise it’s like playing a guitar but hiding your fingers and the strings. When I practice at home I use a Raspberry Pi and a little mini projector on my living room wall. You could use your TV or one of your school/work projectors to give a show. Try it, it’s a lot of fun.

Form a Band

Don’t just play on your own - form a live coding band! It’s a lot of fun jamming with others. One person could do beats, another ambient background, etc. Use the live_audio functionality to combine code with traditional instruments such as a guitar or a microphone.

See what interesting combinations of sounds you can create with code.

TOPLAP

Live coding isn’t completely new - a small number of people have been doing it for a few years now, typically using bespoke systems they’ve built for themselves. A great place to go and find out more about other live coders and systems is TOPLAP.

Algorave

Another great resource for exploring the live coding world is Algorave. Here you can find all about a specific strand of live coding for making music in nightclubs.


- Minecraft Pi

Sonic Pi now supports a simple API for interacting with Minecraft Pi - the special edition of Minecraft which is installed by default on the Raspberry Pi’s Raspbian Linux-based operating system.

No need to import libraries

The Minecraft Pi integration has been designed to be insanely easy to use. All you need to do is to launch Minecraft Pi and create a world. You’re then free to use the mc_* fns just like you might use play and synth. There’s no need to import anything or install any libraries - it’s all ready to go and works out of the box.

Automatic Connection

The Minecraft Pi API takes care of managing your connection to the Minecraft Pi application. This means you don’t need to worry about a thing. If you try and use the Minecraft Pi API when Minecraft Pi isn’t open, Sonic Pi will politely tell you. Similarly, if you close Minecraft Pi whilst you’re still running a live_loop that uses the API, the live loop will stop and politely tell you that it can’t connect. To reconnect, just launch Minecraft Pi again and Sonic Pi will automatically detect and re-create the connection for you.

Designed to be Live Coded

The Minecraft Pi API has been designed to work seamlessly within live_loops. This means it’s possible to synchronise modifications in your Minecraft Pi worlds with modifications in your Sonic Pi sounds. Instant Minecraft-based music videos! Note however that Minecraft Pi is alpha software and is known to be slightly buggy. If you encounter any problems simply restart Minecraft Pi and carry on as before. Sonic Pi’s automatic connection functionality will take care of things for you.

Requires a Raspberry Pi 2.0

It is highly recommended that you use a Raspberry Pi 2 if you wish to run both Sonic Pi and Minecraft at the same time - especially if you want to use Sonic Pi’s sound capabilities.

API Support

At this stage, Sonic Pi supports basic block and player manipulations which are detailed in Section 11.1. Support for event callbacks triggered by player interactions in the world is planned for a future release.


- Basic Minecraft Pi API

Sonic Pi currently supports the following basic interactions with Minecraft Pi:

Displaying chat messages Setting the position of the user Getting the position of the user Setting the block type at a given coordinate Getting the block type at a given coordinate

Let’s look at each of these in turn.

Displaying chat messages

Let’s see just how easy it is to control Minecraft Pi from Sonic Pi. First, make sure you have both Minecraft Pi and Sonic Pi open at the same time and also make sure you’ve entered a Minecraft world and can walk around.

In a fresh Sonic Pi buffer simply enter the following code:

mc_message "Hello from Sonic Pi"

When you hit the Run button, you’ll see your message flash up on the Minecraft window. Congratulations, you’ve written your first Minecraft code! That was easy wasn’t it.

Setting the position of the user

Now, let’s try a little magic. Let’s teleport ourselves somewhere! Try the following:

mc_teleport 50, 50, 50

When you hit Run - boom! You’re instantantly transported to a new place. Most likely it was somewhere in the sky and you fell down either to dry land or into water. Now, what are those numbers: 50, 50, 50? They’re the coordinates of the location you’re trying to teleport to. Let’s take a brief moment to explore what coordinates are and how they work because they’re really, really important for programming Minecraft.

Coordinates

Imagine a pirate’s map with a big X marking the location of some treasure. The exact location of the X can be described with two numbers - how far along the map from left to right and how far along the map from bottom to top. For example 10cm across and 8cm up. These two numbers 10 and 8 are coordinates. You could easily imagine describing the locations of other stashes of treasure with other pairs of numbers. Perhaps there’s a big chest of gold at 2 across and 9 up…

Now, in Minecraft two numbers isn’t quite enough. We also need to know how high we are. We therefore need three numbers:

How far from right to left in the world - x How far from front to back in the world - z How high up we are in the world - y

One more thing - we typically describe these coordinates in this order x, y, z.

Finding your current coordinates

Let’s have a play with coordinates. Navigate to a nice place in the Minecraft map and then switch over to Sonic Pi. Now enter the following:

puts mc_location

When you hit the Run button you’ll see the coordinates of your current position displayed in the log window. Take a note of them, then move forward in the world and try again. Notice how the coordinates changed! Now, I recommend you spend some time repeating exactly this - move a bit in the world, take a look at the coordinates and repeat. Do this until you start to get a feel for how the coordinates change when you move. Once you’ve understood how coordinates work, programming with the Minecraft API will be a complete breeze.

Let’s Build!

Now that you know how to find the current position and to teleport using coordinates, you have all the tools you need to start building things in Minecraft with code. Let’s say you want to make the block with coordinates 40, 50, 60 to be glass. That’s super easy:

mc_set_block :glass, 40, 50, 60

Haha, it really was that easy. To see your handywork just teleport nearby and take a look:

mc_teleport 35, 50, 60

Now turn around and you should see your glass block! Try changing it to diamond:

mc_set_block :diamond, 40, 50, 60

If you were looking in the right direction you might have even seen it change in front of your eyes! This is the start of something exciting…

Olhando os blocos

Let’s look at one last thing before we move onto something a bit more involved. Given a set of coordinates we can ask Minecraft what the type of a specific block is. Let’s try it with the diamond block you just created:

puts mc_get_block 40, 50, 60

Yey! It’s :diamond. Try changing it back to glass and asking again - does it now say :glass? I’m sure it does :-)

Available block types

Before you go on a Minecraft Pi coding rampage, you might find this list of available block types useful:

    :air
    :stone
    :grass
    :dirt
    :cobblestone
    :wood_plank
    :sapling
    :bedrock
    :water_flowing
    :water
    :water_stationary
    :lava_flowing
    :lava
    :lava_stationary
    :sand
    :gravel
    :gold_ore
    :iron_ore
    :coal_ore
    :wood
    :leaves
    :glass
    :lapis
    :lapis_lazuli_block
    :sandstone
    :bed
    :cobweb
    :grass_tall
    :flower_yellow
    :flower_cyan
    :mushroom_brown
    :mushroom_red
    :gold_block
    :gold
    :iron_block
    :iron
    :stone_slab_double
    :stone_slab
    :brick
    :brick_block
    :tnt
    :bookshelf
    :moss_stone
    :obsidian
    :torch
    :fire
    :stairs_wood
    :chest
    :diamond_ore
    :diamond_block
    :diamond
    :crafting_table
    :farmland
    :furnace_inactive
    :furnace_active
    :door_wood
    :ladder
    :stairs_cobblestone
    :door_iron
    :redstone_ore
    :snow
    :ice
    :snow_block
    :cactus
    :clay
    :sugar_cane
    :fence
    :glowstone_block
    :bedrock_invisible
    :stone_brick
    :glass_pane
    :melon
    :fence_gate
    :glowing_obsidian
    :nether_reactor_core