Capítulo 7
Síntese Granular
O método de síntese granular no CSound é uma técnica que oferece resultados
surpreendentes com baixo custo computacional. No tipo de síntese granular
implementado no CSound e no qual estamos interessados, a idéia básica é gerar, a partir
de um som inicial, vários “grãos” desse som encadeados. Normalmente esse sample
inicial dura apenas alguns segundos, como a gravação de uma nota no piano ou uma
senóide gerada no computador.  O sample então é recortado em várias partes e cada uma
delas é modulada por uma função de envelope gerando os grãos. O encadeamento desses
grãos de som gera o efeito sonoro característico da síntese granular. A figura abaixo
mostra uma onda típica de vários grãos encadeados:
Nesse caso mais simples o sample e a forma do envelope fôram uma senóide, sem
haver intervalos de silêncio entre os grãos. A onda de um único grão seria:
Esse caso é útil para dar uma idéia intuitiva e visual do que se trata a síntese
granular, mas ela vai muito além disso. No CSound os samples podem ser qualquer
arquivo de áudio; o tamanho médio de cada grão pode ser definido, assim como o
espaçamento entre eles; a curva de ataque e decaimento pode ser determinada
separadamente, e muitos outros parâmetros. Tipicamente, várias vozes são utilizadas para
gerar o arquivo de saída, o que caracteriza a síntese granular como um método de síntese
aditiva.
Atualmente existem quatro opcodes de síntese granular no CSound: granule,
criado por Allan Lee, grain, de Paris Smaragdis, e grain2 e grain3, de Istvan Varga.
Eles podem ser agrupados como os de fácil utilização, como grain e grain2, e os de
maior controle, como granule e grain3. Vamos começar pelo grain, passando depois por
grain2, granule e grain3, e ao final você terá a escolha de qual se adequa mais ao seu
tipo de som desejado e o controle necessário.
Grain foi o primeiro opcode granular a ser implementado no CSound e é bastante
intuitivo, apesar da quantidade de parâmetros fazê-lo um pouco mais complexo que a
maioria dos opcodes de Csound. A sintaxe de grain é:
ares grain xamp, xpitch, xdens, kampoff, kpitchoff, kgdur, igfn, iwfn,
imgdur [, igrnd]
      
      
Os parâmetros definidos em tempo de inicialização são:
. igfn determina que f-table será usada como sample.
. iwfn diz qual f-table será usada como envelope de amplitude para os grãos.
. imgdur é a duração máxima em segundos de um grão.
. igrand é um parâmtro opcional, com default 0. Para valores diferentes de zero,
ele faz com que grain não leia mais aleatoriamente partes do sample, mas comece sempre
a ler do início. Normalmente é interessante mantê-lo no default (aleatoriedade de leitura),
pois isso é um dos fatores que diferencia o som de grain dos outros opcodes de síntese
granular.
E os parâmetros para tempo de performance são:
.
xamp é a amplitude média dos grãos. Quando a quantidade de grãos simultâneos
aumenta é necessário diminuir esse valor, pois as ondas sobrepostas a aumentarão
involuntariamente.
. xpitch é a freqüência inicial dos grãos. A princípio a freqüência é escolhida pelo
usuário independente da freqüência original do sample, mas para usar a freqüência
original, se o sample possuir exatamente um comprimento de onda, é possível obtê-la
através da fórmula:
Frequência original = Taxa de amostragem  / Tamanho da f-table
De fato, para se chegar a essa fórmula basta lembrar que:
Tamanho da f-table em pontos = Taxa de amostragem  * Período da amostra em
segundos. Isso nos leva a: Tamanho da f-table = Taxa de amostragem / Frequência da
amostra
Na prática, se a taxa de amostragem fôr de 44100, e usando a função ftlen para
obter o tamanho da amostra em pontos, conseguimos a frequência original do sample
com a linha de código:
xpitch = 44100/ftlen(igfn)
. xdens é a densidade  de grãos, diz quantos grãos por segundo a onda de saída
deverá ter.
No caso típico em que xdens é constante, a saída será parecida com a gerada pelo
opcode fof , sendo uma saída de síntese granular síncrona. No caso em que ela é aleatória
a saída será mais parecida com síntese granular assíncrona, e o efeito sonoro é como se
houvesse um ruído adicional.
. kampoff ou amplitude-offset é o deslocamento máximo que as amplitudes dos
grãos poderão ter em relação à amplitude original do parâmetro xamp. Assim a amplitude
de saída variará entre os valores de xamp e xamp+kampoff.
. kpitchoff ou pitch-offset é similar a kampoff, será o deslocamento máximo em
relação à freqüência original, com a freqüência de saída dos grãos variando entre xpitch e
xpitch+kpitchoff..
. kgdur é a duração dos grãos em segundos, limitada superiormente pelo
parâmetro imgdur. Se em algum momento kgdur ultrapassar imgdur, ela é truncada.
No exemplo a seguir veremos a utilização de grain em quatro configurações
diferentes, uma para cada um dos nossos quatro instrumentos. Poderíamos ter feito um
único instrumento e parametrizado na seção de score, mas preferimos colocar as
alterações dentro dos instrumentos, por ser mais claro e organizado.
<CsoundSynthesizer>
<CsOptions>
-o grain.wav
</CsOptions>
<CsInstruments>
sr = 44100
kr = 44100
ksmps = 1
nchnls = 1
; variações leves na frequência e na amplitude dos grãos
instr 1
    ifn = 1
    ienv = 2
    icps = cpspch(p4)
    iamp = ampdb(60)
    idens  = 1600
    iaoff  = ampdb(10)
    ipoff  = 10
    igdur  = .1
    imaxgdur =  .5
    ar  grain iamp, icps, idens, iaoff, ipoff, igdur, ifn, ienv, imaxgdur
    out ar
endin
; variação de até uma oitava na frequência dos grãos
instr 2
    ifn = 1
    ienv = 2
    icps = cpspch(p4)
    iamp = ampdb(50)
    idens = 1600
    iaoff = ampdb(10)
    ipoff = icps
    igdur = .1
    imaxgdur =  .5
    ar  grain iamp, icps, idens, iaoff, ipoff, igdur, ifn, ienv, imaxgdur
    out ar
endin
instr 3
    ifn = 1
    ienv = 2
    icps = cpspch(p4)
    kamp line ampdb(50), p3, ampdb(70)
    kdens linseg 1600, p3, 10
    iaoff  = ampdb(10)
    kpoff line icps, p3, icps*7
    igdur = .1
    imaxgdur =  .5
    ar  grain kamp, icps, kdens, iaoff, kpoff, igdur, ifn, ienv, imaxgdur
    out ar
endin
instr 4
    ifn = 1
    ienv = 2
    icps = cpspch(p4)
    iamp = ampdb(70)
    idens = 10
    iaoff  = ampdb(10)
    ipoff = icps*7
    igdur = .1
    imaxgdur =  .5
    ar  grain iamp, icps, idens, iaoff, ipoff, igdur, ifn, ienv, imaxgdur
    out ar
endin
</CsInstruments>
<CsScore>
; Tabela #1: uma simples onda de seno usando GEN10.
f 1 0 16384 10 1
; Tabela #2: função de envelope usando GEN7.
f 2 0 16384 7 0 4096 5000 4096 2500 4096 2500 4096 0
i 1 0  4 8.09
i 2 5  4 8.09
i 3 10 9 8.09
i 4 20 5 8.09
e
</CsScore>
</CsoundSynthesizer>
Fig. 1: grain.orc: quatro instrumentos diferentes para quatro configurações de grain.
   
No primeiro instrumento temos grain com uma densidade kdens de 1600 grãos
de som por segundo, e uma leve variação ipoff na frequência icps e outra leve variação
iaoff na amplitude. Trata-se de uma síntese granular com densidade regular e amplitude e
frequência quase constantes. Nesse caso podemos perceber a frequência fundamental da
nota, que aqui é A4.
No segundo instrumento temos uma variação de frequência entre a frequência
fundamental icps e sua oitava acima icps + ipoff. Aqui os outros parâmetros permanecem
constantes com uma leve variação iaoff na amplitude iamp. Já não podemos perceber
uma frequência fundamental, dada as variações bruscas na frequência, talvez
assemelhando-se um pouco ao som do vento.
Antes de falarmos do terceiro instrumento, falaremos do quarto por se tratar de
um caso particular do terceiro. Temos aqui um uso bastante não-ortodoxo do opcode
grain. Definimos sua densidade idens para apenas 10 grãos por segundo, cada um com
uma duração média igdur de um décimo de segundo, e a frequência variando entre icps e
icps+ipoff, três oitavas acima da frequência fundamental. Pela baixa densidade e duração
dos grãos, podemos perceber cada grão individualmente, com suas frequências se
alterando aleatoriamente, produzindo um som, dependendo da sua imaginação,
semelhante à uma peça aleatória ou à um computador de seriados antigos de TV.
O terceiro instrumento é uma transição gradual do segundo para o quarto
intrumento,  com a amplitude kamp, a densidade kdens  e o deslocamento de frequência
kpoff variando entre os valores dos dois instrumentos através de segmentos line e linseg.
O score toca os quatro instrumentos com duração de quatro segundos, exceção
feita para o terceiro instrumento, com uma duração maior.
Grain2 foi criado para se dar uma alternativa mais simples a grain3, que
veremos depois.
A sintaxe de grain2 é dada por:
ares grain2 kcps, kfmd, kgdur, iovrlp, kfn, iwfn [, irpow] [, iseed] [,
imode]
Os parâmetros de tempo de inicialização são:
. iovrlp é o número de grãos que podem se sobrepor.
. iwfn é o índice em que está a f-table de envelope.
. irpow é um parâmetro opcional, com valor default 0. Ele determina a
distribuição das frequências pseudo-aleatórias. Para valores de 0, 1 e -1 essa distribuição
é uniforme. 
Para mais detalhes sobre as fórmulas que governam essa distribuição, por favor
consulte o Csound Reference Manual.
. iseed é um valor opcional, com default 0. Ele determina qual será a semente
positiva a ser usada para se começar a geração de números randômicos, podendo ser um
valor de até 32 bits com sinal. Se valores negativos ou zero são usados, a semente será o
tempo atual, que é o default.
. imode também é opcional com default 0, e é a soma dos valores:
* 8 para interpolar os envelopes, fazendo uma passagem suave entre
um e outro
* 4 para não interpolar a forma de onda dos grãos, sem fazer uma
passagem suave
* 2 para modificar a frequência dos grãos sempre que kcps é
modificado, em vez de mudá-la apenas a partir do próximo grão
* 1 para não fazer a inicialização.
Os parâmetros de tempo de performance são:
. kcps é a frequência fundamental dos grãos.
. kfmd é o deslocamento aleatório máximo em Hertz da frequência, para mais
ou para menos.
. kgdur é a duração dos grãos em segundos a cada instante. Internamente, sso é
feito pelo opcode mudando-se a velocidade que ele lê o sample, o que muda inclusive a
duração dos grãos já começados.
. kfn é o índice da f-table contendo o sample. Note que ele pode ser definido em
tempo de performance como um parâmetro k-rate, ao contrário do parâmetro ifn estático
de grain.
Apesar da quantidade de parâmetros de grain2, a maioria é opcional. Portanto
uma versão simplificada da sintaxe de grain2 seria apenas:
ares grain2 kcps, kfmd, kgdur, iovrlp, kfn, iwfn
Vamos fazer agora a versão para grain2 dos instrumentos usados com grain do
exemplo anterior. Teremos novamente quatro instrumentos, para que póssamos fazer uma
comparação direta entre grain2 e grain.
<CsoundSynthesizer>
<CsOptions>
-o grain2.wav
</CsOptions>
<CsInstruments>
sr = 44100
kr = 44100
ksmps = 1
nchnls = 1
giamp = 1000
; Variação de frequência pequena
instr 1
    kcps = cpspch(p4)
    kfmd  = 10 ; variação é bipolar
    kgdur  = .1
    iovrlp = 160 ; 1600 por segundo
    kfn = 1
    iwfn = 2 ; note que não há imaxgdur nem kamp, e iovrlp é i ao invés de k
como kdens
    irpow = 0
    iseed = 0
    imode = 10
    ares grain2 kcps, kfmd, kgdur, iovrlp, kfn, iwfn, irpow, iseed, imode
    out ares*giamp
endin
; variação de frequência média
instr 2
    kcps = cpspch(p4)
    kfmd  = kcps ; agora a variação de frequência é maior
    kgdur  = .1
    iovrlp = 160
    kfn = 1
    iwfn = 2
    irpow = 0
    iseed = 0
    imode = 10
    ares grain2 kcps, kfmd, kgdur, iovrlp, kfn, iwfn, irpow, iseed, imode
    out ares*giamp
endin
; variação de frequência começando pequena e indo a grande
instr 3
    icps = cpspch(p4)
    kfmd line icps, p3, icps * 7  
    kgdur  = .1
    iovrlp = 160 ; nao posso fazer linseg 160, p3, 1
    kfn = 1
    iwfn = 2
    irpow = 0
    iseed = 0
    imode = 10
    ares grain2 icps, kfmd, kgdur, iovrlp, kfn, iwfn, irpow, iseed, imode
    out ares*giamp
endin
; variação de frequência grande, baixa densidade de grãos
instr 4
    kcps = cpspch(p4)
    kfmd = kcps * 7  
    kgdur  = .1
    iovrlp = 1 ; 10 grãos por segundo
    kfn = 1
    iwfn = 2
    irpow = 0
    iseed = 0
    imode = 10
    ares grain2 kcps, kfmd, kgdur, iovrlp, kfn, iwfn, irpow, iseed, imode
    out ares*giamp
endin
</CsInstruments>
<CsScore>
; Tabela #1: uma simples onda de seno usando GEN10.
f 1 0 16384 10 1
; Tabela #2: função de envelope usando GEN7.
f 2 0 16384 7 0 4096 5000 4096 2500 4096 2500 4096 0
; Variação de frequência constante e pequena
i 1 0  4 8.09
; variação de frequência constante e média
i 2 5  4 8.09
; variação de frequência indo de média para grande
i 3 10 9 8.09
; variação de frequência grande, baixa densidade de grãos
i 4 20 4 8.09
e
</CsScore>
</CsoundSynthesizer>
Fig.2: grain2.csd, a versão para grain2 dos quatro instrumentos do
exemplo anterior.
Para que possamos comparar os dois opcodes grain e grain2, é necessário fazer
um parêntese. Em Csound podemos classificar as ondas em dois tipos: unipolar e bipolar.
Ondas unipolares assumem apenas valores positivos, e são usadas normalmente para
envelopes. Ondas bipolares, mais comuns, assumem valores positivos e negativos, como
o caso da senóide. 
Em grain tinhamos o parâmetro kpitchoff, que era a variação da frequência
xpitch. Essa variação era unipolar, ou seja, kpitchoff era sempre positivo e a frequência
variava apenas entre os valores xpitch e xpitch+kpitchoff. Entretanto em grain2 a
variação de frequência kfmd é bipolar, assume valores positivos e negativos. 
Além disso, em grain2 não especificamos diretamente a densidade de grãos por
segundo, como em grain com o parâmetro kdens, mas temos o parâmetro semelhante
iovrlp, que é a quantidade de grãos que podem se sobrepor a qualquer momento. Então
se antes tínhamos uma densidade de grãos kdens de 1600 grãos por segundo, podemos
alcançar essa mesma densidade através da fórmula kdens = iovrlp/kgdur.
Ou seja, apesar de não termos o parâmetro kdens, sabemos que a quantidade de
grãos simultâneos iovrlp dividida pela duração de cada grão kgdur nos dá a densidade de
grãos por segundo kdens. Por exemplo, se geramos dois grãos simultâneos, mas esses
dois grãos durarem 10 segundos até podermos gerar outros dois grãos, teremos uma
densidade de 2 / 10 = 0.2 grãos por segundo. 
No nosso primeiro instrumento instr 1 definimos a duração dos grãos kgdur
como um décimo de segundo, e definimos que haveria 160 grãos sobrepostos a cada
momento, ou seja, iovrlp é de 160 grãos à cada décimo de segundo, o que nos dá uma
densidade de 1600 grãos por segundo. 
Entretanto existe uma diferença adicional que não se pode emular: iovrlp é do
tipo i-rate, é definida apenas na inicialização, enquanto kdens é k-rate, atualizada pela
taxa de controle. Essa será a diferença de parametrização em nosso terceiro instrumento,
pois no exemplo de grain variávamos a densidade gradualmente, e aqui a densidade será
fixa. Afora esse aspecto, podemos fazer uma comparação direta entre os sons produzidos
por grain e grain2.
Outras diferenças que não afetarão nossos instrumentos é que grain2 não possui
os parâmetros imaxgdur nem kamp. De fato não precisamos aqui de imaxgdur, mas
faremos uma leve alteração para usarmos um fator de amplitude em grain2. Basta
pegarmos o sinal de saída ares e multiplicarmos pelo fator de amplitude giamp, como na
linha:  
    out ares*giamp
Para alcançar qualidade máxima na síntese de grain2, definimos imode como
10, isto é, interpolamos o sample e alteramos dinamicamente a frequência dos grãos a
partir de kcps e kfmd
Agora podemos renderizar grain2.wav e notar as diferenças de som entre os
dois opcodes.
Apesar de granule ser mais complexo que grain, a maioria de seus parâmetros
adicionais são de inicialização, não sendo necessário manipulá-los durante a
performance. A sintaxe de granule é:
ares granule xamp, ivoice, iratio, imode, ithd, ifn, ipshift, igskip,    
  igskip_os, ilength, kgap, igap_os, kgsize, igsize_os, iatt, idec 
  [, iseed] [, ipitch1] [, ipitch2] [, ipitch3] [, ipitch4] [, ifnenv]
Os parâmetros de granule são:
. xamp a amplitude.
. ivoice é a quantidade de vozes simultâneas que granule gera. É a versão de
kdens em grain e iovrlp em grain2.
. iratio é a velocidade de leitura relativa à taxa de amostragem sr da onda
armazenada na f-table
Uma iratio de 0.5 seria metade da velocidade, e esticaria um sample de 1
segundo para 2 segundos, ou poderíamos usar uma iratio de 10, que comprimiria esse
sample para um décimo de segundo, e etc.
. imode define a direção de leitura do sample. +1 faz com que a leitura seja
normal para frente, -1 para trás e com 0 a leitura é definida aleatoriamente para cada grão.
. ithd é o valor de threshold, ou limite mínimo de amplitude para o sample. Para
pontos no sample que tiverem amplitude abaixo desse valor, eles serão descartados,
podendo assim pular silêncios e filtrar ruídos. 
. ifn é o índice da f-table que será usada como sample.
. ipshift é o número de frequências diferentes que serão usados nas vozes de
saída. Se ipchift é definida como 0, a frequência de cada grão variará aleatoriamente para
uma oitava acima ou abaixo da frequência fundamental. Ipshift também pode assumir os
valores  1, 2, 3 e 4, sendo esse o número de frequências diferentes para os grãos de saída.
Os valores exatos que cada uma das frequências é definida, é dito nos
parâmetros opcionais  ipitch1, ipitch2, ipitch3, ipitch4. Para um valor de 0.5 por
exemplo, essa frequência será uma oitava abaixo da fundamental, para 2 uma oitava
acima e etc., i.e., os parâmetros ipitch multiplicam a frenquência fundamental. 
. igskip é o tempo em segundos a partir do qual se começa a leitura do sample
para gerar o grão
. igskip_os ou skip offset é o deslocamento aleatório máximo, para mais ou para
menos, do tempo inicial igskip de leitura do sample.
. ilenght é o tempo de leitura da tabela em segundos, começando em
igskip+igskip_os. Se ilength terminar antes do fim da f-table, apenas essa parte da tabela
será usada para gerar o grão.
. kgap o intervalo médio de segundos entre os grãos, sem as alterações dadas
por igap_os.
. igap_os é o deslocamento aleatório do intervalo de tempo entre os grãos, para
mais ou para menos, em termos de porcentagem do intervalo padrão definido em kgap.
Para kgap igual a 1 segundo e igap_os 10, haverá um deslocamento aleatório nesse
tempo de até 10%.
. kgsize é a duração média dos grãos, sem as alterações de igsize_os.
. igsize_os é a variação em porcentagem do tamanho dos grãos definido em
kgsize, semelhante à ação de igap_os em kgap.
. iatt é a percentagem do tempo de duração do grão que será usado para o
envelope de ataque.
. idec é a percentagem da duração do grão destinada para o decaimento.
. iseed é opcional, com default 0.5. É o valor inicial para o gerador de números
randômicos.
. ipitch1, ipitch2, ipitch3, ipitch4 são parâmetros opcionais com default 1. Para
mais detalhes veja ipshift acima.
. ifnenv é opcional, com default 0 É a f-table usada como envelope dos grãos,
para índices diferentes de 0. Normalmente não é necessário, pois iatt e idec já dão um
envelope.
Como visto acima granule tem várias diferenças de grain e grain2. O que
antes eram versões para densidade de grãos, aqui é o número de vozes independentes,
podendo ter até quatro frequências fundamentais diferentes. Dessa maneira, podemos
gerar tríades e tetracordes a partir de um único granule
Mas granule tem uma desvantagem, já que em nenhum parâmetro é dada a
frequência fundamental dos grãos, e a frequência é exatamente a da onda contida na f-
table usada para gerar os grãos. Assim, se na f-table estiver contida uma senóide de 440
Hz, essa será a frequência fundamental dos grãos. Em nosso exemplo usaremos como
sample uma f-table que lê o arquivo seno.wav, que é a senóide gerada pelo primeiro
instrumento desse livro. 
<CsoundSynthesizer>
<CsOptions>
-o granule.wav
</CsOptions>
<CsInstruments>
sr = 44100
kr = 44100
ksmps = 1
nchnls = 1
instr 1
kamp = 1000
ivoice = 100
iratio = 1
imode = 0
ithd = 0
ifn = 1
ipshift = 2
igskip = 0
igskip_os = 0.005
ilength = 2
kgap = 0.01
igap_os = 50
kgsize = 0.05
igsize_os = 50
iatt = 50
idec = 50
iseed = 0.5
ipitch1 = cpspch(p4)/440
ipitch2 = cpspch(p5)/440
a1 granule kamp, ivoice, iratio, imode, ithd, ifn, ipshift, igskip, \
igskip_os, ilength, kgap, igap_os, kgsize, igsize_os, iatt, \
idec, iseed, ipitch1, ipitch2
out a1
endin
</CsInstruments>
<CsScore>
; Tabela #1: leitura de uma senóide de 440 Hz usando GEN01.
f 1 0 262144 1 "seno.wav" 0 0 0
; repetir a seção 4 vezes
r 4
i 1 0 1 8.05 8.09
i 1 1 1 8.02 8.09
i 1 2 1 8.05 8.09
i 1 3 1 8.04 8.07
i 1 4 1 8.05 8.09
i 1 5 1 8.07 8.11
i 1 6 1 8.05 9.02
i 1 7 1 8.07 8.11
i 1 8 1 8.04 9.04
i 1 9 1 8.09 9.00
i 1 10 1 7.11 8.11
; fim da seção
s
i 1 0 1 8.05 8.09
e
</CsScore>
</CsoundSynthesizer>
Fig. 3: granule.csd, acordes gerados a partir de um único opcode granule.
Apenas recapitulando. Em nosso instrumento na Fig.3 usamos o opcode
granule como segue:
- 100 vozes simultâneas em ivoice
- Leitura sem modificar a frequência original em iratio
- Direção de leitura aleatória em imode
- Sem limite mínimo de amplitude ithd para descartar pontos
- Duas frequências ipshift diferentes nas vozes
- Começar a ler do ínicio do sample em igskip...
- ...com uma variação no tempo de leitura inicial de 0.005 segundos em
igskip_os...
-
...e a partir daí ler 2 segundos do sample, como especificado em ilenght.
- Um intervalo entre grãos padrão de 0.01 segundos em kgap...
- ...e variações nesse intervalo de 50% em igap_os.
- Tamanho padrão de grãos kgsize de 0.05 segundos...
- ...e variações de tamanho igsize_os de 50%.
- 50% da duração dos grãos para o ataque em iatt...
- ...e idec também de 50% para o decaimento.
-
O valor opcional da semente de números randômicos iseed é o default de 0.5.
-
Finalmente definimos os valores das frequências ipitch1 e ipitch2 para formar
os acordes definidos no score nos parâmetros p4 e p5 que estão no formato octave-point-
pitch-class. Como a frequência final será ipitch1*frequência da f-table, para obtermos a
frequência desejada de cpspch(p4), temos que definir ipitch1 como cpspch(p4) /
frequência da f-table, e fazer o mesmo com ipitch2, como ficou na nossa orchestra:
ipitch1 = cpspch(p4)/440
ipitch2 = cpspch(p5)/440
Em nosso score usamos duas novas declarações, r e s. A primeira declaração é
da forma r p1 e repete o trecho a seguir p1 vezes até encontrar uma outra declaração r, s
ou e. A declaração s marca o fim da seção, e se ela estiver sendo repetida retorna ao r
anterior. Nesse score repetimos quatro vezes o trecho entre r e s, notando que quando a
seção termina, os tempos de início das notas seguintes são reinicializados para zero.
Assim nosso score toca aproximadamente o trecho abaixo, um belo tema a duas
vozes:
Fig. 4: a partitura aproximada do score de granule.
Em muitos aspectos grain3 é semelhante a grain2, basicamente trata-se de uma
extensão, com parâmetros para variação de fase e de densidade que não haviam antes.
Alguns desses parâmetros já são familiares para o leitor:
ares grain3 kcps, kphs, kfmd, kpmd, kgdur, kdens, imaxovr, kfn, iwfn,\                
            kfrpow, kprpow [, iseed] [, imode]
 
Parâmetros de inicialização são:
. imaxovr é a quantidade máxima de grãos sobrepostos a qualquer momento.
Esse número poderia ser obtido através de kgdur*kdens, mas via de regra esse cálculo
superestima a quantidade de grãos simultâneos, tornando o processamento mais lento.
. iwfn é o índice da f-table contendo a onda de envelope.
. iseed é a semente de geração de números randômicos, opcional com default 0.
. imode também é opcional com default 0, é a soma dos valores abaixo, e a
partir do valor 8 é idêntica a grain2
* 64 para sincronizar a fase inicial dos grãos com kcps
* 32 para começar todos os grãos em valores inteiros de pontos no
sample
* 16 não renderiza grãos com tempo de início negativo
* 8 para interpolar os envelopes
* 4 para não interpolar a forma de onda dos grãos
* 2 para modificar a frequência dos grãos sempre que kcps é
modificado
* 1 para não fazer a inicialização.
Os parâmetros de performance são:
. kcps é a frequência fundamental dos grãos
. kphs é a fase padrão em que os grãos devem começar. Kphs é um valor entre
0 e 1 que diz em qual fração do comprimento da tabela começará a ser feita a leitura.
. kfmd é a variação aleatória em Hertz que a frequência pode ser alterada, e é
bipolar (para mais ou para menos).
. kpmd é a variação aleatória bipolar em que a fase pode ser alterada.
. kgdur é a duração dos grãos em segundos.
. kdens é a densidade de grãos por segundo.
. kfrpow é o índice que controla a distribuição das frequências aleatórias, favor
consultar irpow em grain2.
. kprpow controla a distribuição de fases aleatórias, semelhante a irpow em
grain2.
. kfn é o índice da f-table da forma de onda dos grãos.
Em nosso exemplo usaremos todas as possibilidade de variações em grain3
com a exceção apenas de variar a forma de onda kfn. Usamos quatro instrumentos como
em grain e grain2.
<CsoundSynthesizer>
<CsOptions>
-o grain3.wav
</CsOptions>
<CsInstruments>
sr = 44100
kr = 4410
ksmps = 10
nchnls = 1
; variação de fase
instr 1
    kamp = 1000
    kcps = cpspch(p4)
    kphs line 0, p3, 1
    kfmd = 10
    kpmd = 0
    kgdur = .1
    kdens = 160
    imaxovr = 160
    kfn = 1
    iwfn = 2
    kfrpow = 0
    kprpow = 0
    iseed = 0
    imode = 10
    ares  grain3 kcps, kphs, kfmd, kpmd, kgdur, kdens, imaxovr, kfn, \
  iwfn, kfrpow, kprpow, iseed, imode
    out ares*kamp
endin
; densidade aumentando
instr 2
    kamp line 1000, p3, 5000
    kcps = cpspch(p4)
    kphs = 0.5
    kfmd = kcps  ; agora a variação de frequência é maior
    kpmd = 0.5
    kgdur line 1, p3, .1
    kdens linseg 1, p3/5, 10, p3/5, 100, p3*3/5, 1600
    imaxovr = 1600
    kfn = 1
    iwfn = 2
    kfrpow = 0
    kprpow = 0
    iseed = 0
    imode = 10
    ares  grain3 kcps, kphs, kfmd, kpmd, kgdur, kdens, imaxovr, kfn, \
  iwfn, kfrpow, kprpow, iseed, imode
    out ares*kamp
endin
; frequência aumentando
instr 3
    kamp line 1000, p3, 5000
    icps = cpspch(p4)
    kphs = 0.5
    kfmd line icps, p3, icps * 7  
    kpmd = 0.5
    kgdur line .1, p3, 1
    kdens linseg 1600, p3/2, 10, p3/2, 1
    imaxovr = 1600
    kfn = 1
    iwfn = 2
    kfrpow = 0
    kprpow = 0
    iseed = 0
    imode = 10
    ares  grain3 icps, kphs, kfmd, kpmd, kgdur, kdens, imaxovr, kfn, \
  iwfn, kfrpow, kprpow, iseed, imode
    out ares*kamp
endin
; variação de frequência grande, baixa densidade de grãos 
instr 4
    kamp line 1000, p3, 5000
    kcps = cpspch(p4)
    kphs = 0.5
    kfmd = kcps * 7  
    kpmd = 0.5
    kgdur line .1, p3, 1
    kdens line 10, p3, 1
    imaxovr = 10
    kfn = 1
    iwfn = 2
    kfrpow = 0
    kprpow = 0
    iseed = 0
    imode = 10
    ares  grain3 kcps, kphs, kfmd, kpmd, kgdur, kdens, imaxovr, kfn, \
  iwfn, kfrpow, kprpow, iseed, imode
    out ares*kamp
endin
</CsInstruments>
<CsScore>
; Tabela #1: uma simples onda de seno usando GEN10.
f 1 0 16384 10 1
; Tabela #2: função de envelope usando GEN7.
f 2 0 16384 7 0 4096 5000 4096 2500 4096 2500 4096 0
; variação de fase
i 1 0  9 8.09
; densidade aumentando
i 2 10 9 8.09
; variação de frequência e duração aumentando
i 3 20 9 8.09
; variação de frequência grande, baixa densidade de grãos
i 4 30 9 8.09
e
</CsScore>
</CsoundSynthesizer>
Fig. 5: grain3.csd, variações de densidade, frequência e durações
No primeiro instrumento variamos apenas a faze kphs, no segundo mantemos a
variação de frequência kfmd constante, mas começamos com a duração dos grãos kgdur
em um segundo e ao longo da nota muda para um décimo de segundo. A variação de
densidade kdens foi feita em etapas com uma declaração linseg: .
    kdens linseg 1, p3/5, 10, p3/5, 100, p3*3/5, 1600
No terceiro instrumento variamos simultaneamente a variação de frequência 
kfmd, a duração kgdur e a densidade kdens, com kfmd indo de uma oitava acima e
abaixo, e aumentando até três oitavas com kcps*7. A duração e a densidade fazem o
caminho inverso do segundo instrumento, com kgdur indo de um décimo para um
segundo e kdens indo de 1600 para 1 grão por segundo.
No quarto instrumento temos a nova versão do que foi usado em grain2, dessa
vez além da variação de frequência ser grande e a densidade baixa, os grãos vão ficando
com duração maior, dando uma sensação de retardo no andamento do tempo.
A técnica de síntese do opcode fof pode ser considerada de síntese granular,
entretanto isso só ocorre com baixas frequências fundamentais. Fof gera um trem de
pulsos como os opcodes anteriores, e a frequência fundamental aqui é na verdade a
quantidade de pulsos gerados por segundo, isto é, ela  se comporta como densidade dos
grãos. Quando essa frequência aumenta ela deixa de ser a densidade e passa a ser
realmente a fundamental de um outro tipo. Em outras palavras, quando temos frequências
abaixo de 30 Hz, o opcode fof se comporta como de síntese granular, quando ela
aumenta, temos a síntese timbral. 
Essa dualidade do opcode fof o torna mais conveniente para esse segundo tipo
de síntese, e em comparação com os opcodes anteriores ele possui vários parâmetros para
controlar detalhadamente cada grão, mudando assim o timbre final. 
A sintaxe de fof é:
ares fof xamp, xfund, xform, koct, kband, kris, kdur, kdec, iolaps,   
ifna, ifnb, itotdur [, iphs] [, ifmode] [, iskip]
Os parâmetros de inicialização são:
. iolaps é a quantidade máxima de grãos sobrepostos em qualquer momento.
Pode ser estimada efetuando-se xfund*kdur, assim como fazíamos xdens*kgdur em
grain.
. ifna é o índice da f-table que contém a forma de onda do grão
. ifnb é o índice que contém a forma de onda do envelope de ataque, e é feita
sua reflexão para usar como envelope de decaimento.
. itotdur é a duração total do trem de pulsos, geralmente é o valor de p3.
Os três parâmetros de inicialização restantes são opcionais:
. iphs é a fase em que se inicia ifna e deve estar entre 0 e 1, tem default 0.
. ifmode nos diz, para valores positivos, se a frequência de um grão é variável
durante a duração de kdur, seguindo a variação de frequência xform, e para o valor 0
(default) permanece com a frequência xfund
Para que a frequêncie do grão varie continuamente, ifmode deve ser 1.
. iskip é por default 0, se fôr não-zero a inicialização é pulada.
Os parâmetros de tempo de performance são:
. xamp é a amplitude.
. xfund é a frequência de grãos por segundo. Para valores abaixo de 30 Hz
se comporta como a densidade de grãos. Para valores acima, se torna a frequência 
fundamental da nota.
. xform é a frequência formante, ou seja, o harmônico mais alto que o grão deve
ter.
. koct é o parâmetro de oitavação, normalmente 0. Para um número inteiro x,
temos que a frequência fundamental é deslocada x oitavas para baixo. Para números
fracionados, temos as frequências intermediárias entre as oitavas. O parâmetro koct
realiza esse deslocamento atenuando grãos alternados para cada valor inteiro.
. kband normalmente é 0, para valores maiores ele acelera o decaimento natural
dos grãos, antes da aplicação do envelope. Quanto maior kband, maior e mais rápida a
atenuação.
. kris é o tempo de ataque do envelope em segundos.
. kdur é a duração dos grãos.
. kdec é o tempo de decaimento.
O opcode fof tem algumas semelhanças com os opcodes de síntese granular que
vimos anteriormente. Neles havia uma variação assíncrona na emissão dos grãos, com
duração e frequência variável, agora não possuímos mais esse controle, gerando um trem
de pulsos síncronos. Por outro lado, temos mais controle do espectro de frequências de
cada grão, especificando a fundamental e a frequência formante. Essa propriedade será
aplicada para gerar síntese imitativa usando fof.
No código que veremos usamos o opcode table, que acessa diretamente um
ponto em uma f-table através da sintaxe:
ires table indx, ifn
em que simplesmente armazenamos em ires o valor da posição indx da f-table ifn.
O opcode fof é muito usado para fazer síntese imitativa da voz humana. Isso é
feito através de uma tabela, desenvolvida pra esse propósito, de cinco frequências
formantes, cada uma com sua amplitude e banda. Isto é, cada vogal “a”, “e”, “i”, “o” ou
“u” emitida, cada uma, tem cinco frequências formantes, com suas cinco amplitudes e
cinco bandas.
Contruiremos as tabelas de formantes, amplitude e banda no score usando a
GEN02, em que o valor de cada ponto é um parâmetro explicitado em sua declaração.
Então na linha:
; baixo "a"
f40 0 16 -2 600 1040 2250 2450 2750 0  -7  -9  -9 -20 60  70 110 120 130
teremos criado uma tabela não-normalizada (-2) com 15 elementos, cada posição
contendo o valor definido nos parâmetros.
Os valores “600 1040 2250 2450 2750” são as cinco frequências formantes
necessárias pra gerar a vogal “a”.
Em seguida “0  -7  -9  -9 -20” são as cinco amplitudes respectivas, e por fim
60  70 110 120 130”  as cinco bandas.
Esses valores serão usados nas cinco instâncias de fof que precisaremos pra
gerar, em nosso exemplo, a vogal “a”:
  a1 fof iamp1, kfund, iform1, 0, iband1, iris, idur, idec, iolaps, ifna,\   
  
      ifnb, itotdur
  a2 fof iamp2, kfund, iform2, 0, iband2, iris, idur, idec, iolaps, ifna,\ 
      ifnb, itotdur
  a3 fof iamp3,  kfund, iform3, 0, iband3, iris, idur, idec, iolaps, ifna,\ 
      ifnb, itotdur
  a4 fof iamp4, kfund, iform4, 0, iband4, iris, idur, idec, iolaps, ifna,\ 
      ifnb, itotdur
  a5 fof iamp4, kfund, iform5, 0, iband5, iris, idur, idec, iolaps, ifna,\ 
      ifnb, itotdur
  out 10000*(a1+a2+a3+a4+a5)
No código abaixo, vamos escolher a vogal pra cada nota, através do parâmetro p5, que
variará entre 40 e 44, respectivos pra cada vogal.
<CsoundSynthesizer>
<CsOptions>
-o fof.wav
</CsOptions>
<CsInstruments>
sr = 44100
kr = 44100
ksmps = 1
nchnls = 1
instr  1
  ; frequência formante, amplitude e banda para cada vogal
  iform1 table    0, p5
  iform2 table    1, p5
  iform3 table    2, p5
  iform4 table    3, p5
  iform5 table    4, p5
  iamp1  table    5,p5
  iamp2  table    6,p5
  iamp3  table    7,p5
  iamp4  table    8,p5
  iamp5  table    9,p5
  iband1 table    10,p5
  iband2 table    11,p5
  iband3 table    12,p5
  iband4 table    13,p5
  iband5 table    14,p5
  iamp1 =        ampdb(iamp1)
  iamp2 =        ampdb(iamp2)
  iamp3 =        ampdb(iamp3)
  iamp4 =        ampdb(iamp4)
  iamp5 =        ampdb(iamp5)
  ; vibrato para dar mais realismo ao vocal
  kvib lfo 10, 5, 1
  kfund = cpspch(p4)+kvib
  ; parâmetros comuns às cinco componentes da vogal
  ioct = 0
  iris = 0.003
  idur = 0.02
  idec = 0.007
  iolaps = 50
  ifna = 1
  ifnb = 3
  itotdur = p3
  ; finalmente a geração da vogal através de suas cinco componentes
  a1 fof iamp1, kfund, iform1, 0, iband1, iris, idur, idec, iolaps, ifna,\   
  
      ifnb, itotdur
  a2 fof iamp2, kfund, iform2, 0, iband2, iris, idur, idec, iolaps, ifna,\ 
      ifnb, itotdur
  a3 fof iamp3,  kfund, iform3, 0, iband3, iris, idur, idec, iolaps, ifna,\ 
      ifnb, itotdur
  a4 fof iamp4, kfund, iform4, 0, iband4, iris, idur, idec, iolaps, ifna,\ 
      ifnb, itotdur
  a5 fof iamp4, kfund, iform5, 0, iband5, iris, idur, idec, iolaps, ifna,\ 
      ifnb, itotdur
  out 10000*(a1+a2+a3+a4+a5)
endin
</CsInstruments>
<CsScore>
f1 0 8192 10 1
; envelope de ataque e decaimento
f3 0 8193 7 0 8192 1000
; baixo "a"
f40 0 16 -2 600 1040 2250 2450 2750 0  -7  -9  -9 -20 60  70 110 120 130 
; baixo "e"
f41 0 16 -2 400 1620 2400 2800 3100 0 -12  -9 -12 -18 40  80 100 120 120
; baixo "i"
f42 0 16 -2 250 1750 2600 3050 3340 0 -30 -16 -22 -28 60  90 100 120 120
; baixo "o"
f43 0 16 -2 400  750 2400 2600 2900 0 -11 -21 -20 -40 40  80 100 120 120
; baixo "u"
f44 0 16 -2 350  600 2400 2675 2950 0 -20 -32 -28 -36 40  80 100 120 120
i1
0
4
7.04
40
i1
4
3.5
7.05
41
i1
7.5
0.5
7.05
40
i1
8
3
7.07
42
i1
11
1
7.02
42
i1
12
4
7.04
40
e
</CsScore>
</CsoundSynthesizer>
Fig.6: fof.csd