imprimir
Introdução ao Python para a Manipulação Sonora

Renato Fabbri e Fábio Furlanete 2007

Colaboradores Marília Chiozo

Licença Creative Commons Atribuição-Uso Não-Comercial-Compartilhamento pela mesma Licença 2.5 Brasil

renato (ponto) fabbri (no) gmail (ponto) com



Até a parte 3 está ok! anotar modificações na lista To-Do (para que sejam revisadas as últimas)!





Notas Preliminares:

Este texto tem como alvo não programadores e também pessoas já experientes na elaboração de programas interessadas em ter alguma idéia das ferramentas mais utilizadas para a programação em áudio envolvendo o Python. Embora este tutorial procure abranger utilizações diversas da linguagem Python, os recursos aqui explorados são comparativamente mais versáteis do que otimizados em termos de tempo de processamento, pois o intuito dos autores é primeiramente a pesquisa.
Este trabalho foi parcialmente apresentado na forma de um workshop em Berlim, no dia 23 de Março de 2007. A autoria inicial é de Renato Fabbri em colaboração com Fábio Furlanete. Para este trabalho aqui escrito, foram recolhidas várias fontes e notas do autor principal. Uma tradução integral das notas ainda não foi desenvolvida (alguém se habilita?).
É importante enfatizar que este tutorial não pretende esgotar as possibilidades do uso do Python para a manipulação de áudio. O Python é uma linguagem de programação estabelecida e versátil, e, portanto, existem diversos módulos disponíveis para multimedia. Mesmo que os módulos escolhidos para esta exposição sejam muito significativos, eles não englobam todas as possibilidades. Não vemos aqui, por exemplo e, o uso do Python para áudio na Internet (por enquanto).
Perguntas, contribuições e sugestões são mais do que bem vindas. Faça contato com os autores/colaboradores junto ao título.

ToDo

(adicionar itens, e o controle de modificações para que sejam revisadas as mais recentes)
  • Discorrer sobre os programas que utilizam o PySndObj?.
  • Fazer um tutorialzinho de instalação do PySndObj?.
  • Fazer Lookup Tables com o Numpy.
  • Utilizar o Pyaudiolab para escrever arquivos de som a partir dos vetores NumPy?.
  • Utilizar o C-Types para alguns programas. E comparar a velocidade com o C puro e com o Python puro.
  • Introduzir o uso do Csnd, para acessar o Csound pelo Python.
  • Adicionar bibliotecas e programas mais complexos.
  • Fazer uma lista de links importantes.
  • Colocar o link para a lista do python sound project.

Done

  • Fazer um Índice. (ok cris, 11/10/2007, maketok!!)
  • Colocar num wiki. (ok gk, 07/10/2007)
  • Revisão e ampliação até o final da parte 2 (Marília e gk ok, 12/10/2007)
  • Fazer subdivisões das partes e sub-partes (ok gk, 13/10/2007).

Introdução: Sobre a Computação Musical

(corrigir a gosto)
A organização e a análise de parâmetros que controlam componentes psicoacústicas do fenômeno sonoro através de computadores é uma marca da produção musical recente em todos os seus campos: gravação, instalações sonoras, composição, jingles, trilhas, shows e apresentações. Também do consumo da música, como por exemplo através dos meios de reprodução do áudio digital, dos equalizadores, etc. Processos de modificação do material sonoro como os simuladores de reverberação, os ecos, os vibratos, os tremollos, o chorus e outros, são apenas alguns já bem conhecidos efeitos provenientes da tradição dos estúdios analógicos. Outros processos de elaboração mais recentes e de origem, muitas vezes, na própria computação são comumente encontrados como plugins (pequenos aplicativos de funcionalidade restrita que rodam dentro de outros programas como sistemas de edição e seqüenciamento sonoro). A síntese sonora por computadores, devido à evolução crescente da tecnologia digital, é, desde o trabalho de B. Truax, processada em tempo real com uma quantidade massiva de dados.

A quantidade de novas ferramentas e novos desenvolvedores tem se multiplicado tanto pela demanda provinda do "mercado musical" quanto pela proximidade que o músico tem com os variados aplicativos necessários para sua atividade. Grosso modo, seqüenciadores, sintetizadores, editores de som, samplers e os simuladores de fenômenos físicos são utilizados extensivamente por músicos e pesquisadores em Computação Musical, Acústica e áreas afins, levando muitos deles a desenvolver novos aplicativos para a criação artística. Em corrente uso profissional encontramos programas versáteis como o PD (Pure Data), o Max/MSP, e o SpiralSynthModular?, cujo aspecto comum e saliente entre eles é explorarem a capacidade modular da sua arquitetura, proporcionando ao usuário a capacidade de criar verdadeiros aplicativos dedicados à tarefas que visadas. Ainda podemos citar o MATLAB que, embora seja um software de manipulação matemática, possui toolboxes relacionados a Processamento de Sinais, bem como várias bibliotecas externas.

Por outro lado, linguagens de programação como o Haskell, o C e o C++, o Java, e o Python possuem bibliotecas com diversos objetos e funcionalidades próprias para o processamento de áudio. Outras linguagens como o SuperCollider? e o Csound foram desenvolvidas desde suas origens como linguagens de programação para música e áudio, objetivando especialmente a música eletroacústica e experimental.

O músico tem se aproximado cada vez mais dos recursos computacionais, mesmo os mais avançados. Notamos a proliferação de empresas que desenvolvem programas para estes usuários, como a Native Instruments ou a Ableton, de linguagens de programação voltada ao áudio, como o SuperCollider?, e de usuários que visam tomar proveito da versatilidade do computador, aprendendo inclusive a programar.

Este trabalho visa facilitar o desenvolvimento destes usuários, e, ao mesmo tempo, oferecer uma visão geral de como se pode trabalhar com o Python, uma linguagem de programação de simples aprendizado, através de capítulos simples, para serem lidos e testados.

1- Instalação

(corrigir a gosto)
O Python é um programa livre, desenvolvido em código aberto, que pode ser utilizado por qualquer pessoa ou grupo de pessoas sem a necessidade de um pagamento.

1.1 – Instalação no Windows ou no MacOSX

Basta baixar o arquivo no sítio http://www.python.org/download/ e instalar.
Ouvi dizer que o OSX já vem com o Python (alguém sabe?). Abra uma Console e digita python pra ver se já está instalado.

1.2 – Instalação em Sistemas Linux

Toda distribuição Linux que conheço já tem o python instalado.
Para bibliotécas adicionais (Aqui utilizamos o Numpy e o PySndObj? mas só a partir da parte quatro), procure na rede e instale normalmente ou procure o pacote adequado para a sua distribuição, .deb para Debian ou Ubuntu, .rpm para Fedora Core, etc. Caso não haja este pacote ou ele não funcione corretamente, procure o código fonte em http://www.python.org/download/ e instale. Não esquecendo de instalar as dependencias necessárias. Não vi um pacote PySndObj? ainda.

1.3 - Linux/Winows/Mac - IDLE

A instalação padrão do python inclue uma IDE (Integrated Development Environment) chamado IDLE, que é um editor de programas e terminal interativo (responde aos comandos assim que você os escreve). Alguns editores de texto são muitos utilizados como o Vi(m) e o Emacs. Eu, Renato, uso o (g)vim de vez em quando, e confesso preferir o Kate mesmo. Se você não quiser usar o IDLE por qualquer motivo, basta digitar python em um terminal (ou prompt no windows) para obter o terminal interativo do Python. Para rodar programas inteiros, faça um arquivo no editor de texto que preferir, salve o arquivo com a terminação .py e rode no terminal assim:

>>> python arquivodetexto.py


2 – Algumas Palavras sobre o Python

(corrigir a gosto)

2.1- Python é (entre outras coisas):


  • Uma Linguagem de Script: Uma classe de linguagens de programação tipicamente interpretada, ou seja, ela não é compilada para código de máquina antes de ser executada. Também conhecida simplesmente por linguagem de computador interpretada. Em cada execução o Python invoca seu interpretador, que interpreta linha por linha do código escrito. Linguagens de script são fortes em se comunicar com componentes escritos em outras linguagens, o Python, a linguagem em questão, importa qualquer biblioteca C ou C++, por exemplo.
  • Tipificação Dinâmica de Variáveis: Isto é, não requer que o programador explicite quais tipos de dados serão processados numa certa expressão. No Python, nem mesmo as variáveis precisam ser declaradas. Vejamos isso.
É altamente recomendado que todas as linhas de código sejam realmente escritas e testadas em um terminal python, sem copiar e colar

Abra o IDLE para testar alguns comandos no terminal interativo:

>>> x = 4 ## Escreva "x = 4" e aperte <Enter>.
                  ## Ao nome do lado esquerdo é atribuído o valor do lado direito.
>>> x ## Escreva "x" e aperte <Enter>.
4
>>> type(x) ## Escreva .... você entendeu, né?
<type 'int'>  ## Não foi preciso dizer ao Python que x era um inteiro
>>> y=3.75 ## Também exitem inteiros tipo long, mas decimais são 'ponto flutuante' por padrão
>>> y
3.75
>>> type(y)
<type 'float'> ## 'Ponto flutuante', tipo de dado usado para representar um número decimal no Python.
>>> z  =  x + y ## Ao nome do lado esquerdo é atribuído o valor resultante do lado direito.
>>> z
7.75
>>> type(z)  ## Soma de um inteiro com um decimal é um decimal.
<type 'float'>
>>> h=1.0 ## Qualquer sequencia de inteiros separados por um ponto é ponto flutuante.
>>>type(h)
<type 'float'>


Aperte ctrl+n ou vá em file->New Window. Escreva:

# Programa geral1.py
# Isso é chamado de "comentario"
# Não precisa compiar linhas que começem com '#' :-)

## objetos nao sao contidos entre aspas,
## objetos de tipo strings sao contidos entre aspas simples ''
## e sao muito utilizados para palavras/indentificadores etc
## veremos outros tipos de dados depois.
## Tente entender com o que já sabe

word = 'bumbble'
num = 128
alist = ['fred', 5, 78.9]

print 'Coconut', word
print 'minha lista eh:', alist

print type(word), 'eh o tipo do objeto word'
print type(num), 'eh o tipo do objeto num'
print type(alist), 'eh o tipo do objeto word'


O comando print é utilizado para mostrar na tela os dados que o seguem na mesma linha. você separa requisições de print para variáveis diferentes com uma vírgula.

Aqui cabe uma observação, tente não utilizar acentos em programas enquanto não precisar, eles requerem um tratamento especial. O objeto alist é um objeto do tipo 'lista', que será visto em maiores detalhes na seção 2.2.
Aperte F5, salve com o nome que quiser e onde quiser e rode o programa. Olhe para o terminal em que testamos a criação de variáveis há pouco. Verá algo assim:

Coconut bumbble
minha lista eh: ['fred', 5, 78.900000000000006]
<type 'str'> eh o tipo do objeto word
<type 'int'> eh o tipo do objeto num
<type 'list'> eh o tipo do objeto word


Pronto. Estes testes já nos dão uma contextualização sobre alguns dos aspectos principais do Python. É muito bom para prototipagem pois é simples e claro. Pode-se começar a escrever o código sem saber direito onde vai chegar, o que não costuma a ser o caso em outras linguagens como o C ou C++. Repare que criamos objetos, operamos sobre eles e os dispusemos na tela. Tudo isso sem pensar sobre declaração de variáveis, alocação de memória ou compilação (para quem não está acostumado com programação, saibam que são partes que demandam muito esforço por parte do programador). Essas características tornam códigos escritos em Python simples, limpos, rápidos para escrever e ler, e sempre prontos para rodar.

2.2 – Objetos (e Listas)


No Python, tudo é um objeto. Isso significa, entre outras coisas, que tudo possui membros: métodos e atributos (que são em muitos aspectos parecidos com variáveis). Métodos e atributos são também objetos e, assim, possuem membros próprios. Ad nauseam. Membros são acessados através do seguinte formato, não esquecendo da diferença entre o acesso a um 'método()' e o acesso a um 'atributo':

objeto.método(argumentos)



ou

objeto.atributo



Isso promove diferenças muito característica na linguagem, e linguagens que são baseadas em objetos são chamadas de Linguagem de Programação Orientada a Objetos (OOP).

A seguir será introduzido o uso de objetos do tipo Lista, que são estruturas de dados muito recorrentes na programação.


>>> l = ['a',7,'e aih veio'] ## criando um novo objeto
>>> type(l) ## checando o tipo do objeto
<type 'list'>
>>> l ## o que l significa
['a', 7, 'e aih veio']
>>> dir(l)  ## dizer quais são os membros (métodos e os atributos) de l que estão disponíveis
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__str__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
>>> l.append ## me dê o valor deste atributo ou diga-me se é um método
<built-in method append of list object at 0xb7dd408c>
>>> l.append()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: append() takes exactly one argument (0 given
>>> l.append.__doc__ ## ok, é um método, então vamos ler seu atributo __doc__ que é sua documentação
'L.append(object) -- append object to end'
>>> l.append(1) ## agora informado pelo __doc__, adicionamos um elemento ao fim da lista
>>> l ## mostre-me como ficou a lista
['a', 7, 'e aih veio', 1]



Todas as funcionalidades de um objeto constam em seus membros e algumas possuem "atalhos" utilizados através de sintaxe particular:


>>> l.__getitem__ ## O que é o membro __getitem__ do objeto l
<built-in method __getitem__ of list object at 0xb7cf20cc>
>>> l.__getitem__.__doc__ ## Qual a sua 'doc string'?
'x.__getitem__(y) <==> x[y]'
>>> l.__getitem__() ## Vamos chamá-lo como método e nenhum argumento, dá pau, erro
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: __getitem__() takes exactly one argument (0 given)
>>> l.__getitem__(2) ## Vamos chamá-lo como método e com um argumento
'e aih veio'
>>> l[2] ## Atalho sintático para o método: l.__getitem__():  l[]
'e aih veio'
>>> l[2] ='porororor' ## O item no lugar 2 (  [0,1,2,3]  índice 2 da lista  )
>>> l
['a', 7, 'porororor', 1]
>>> l[10] ## Não se pode invocar elemento índice que não existe.
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
IndexError: list index out of range
>>> lista_legal[4] ## também não se pode pedir nada de um objeto que aonda não existe, erro.
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'lista_legal' is not defined

>>> lista_legal = 'jardinagem','funelaria','janta','baladinha','soneca'
>>> lista_legal 4
'soneca'

O método getitem(x) é muito útil na manipulação de listas. Assim, este tipo de objeto possui o atalho sintático
l.__getitem__(x) equivale a l[x], ambos acessam o membro da lista de índice x.

A indexação dos elementos de uma listas no python é feita por meio de um número inteiro que indica a posição na lista. O primeiro elemento tem índice 0 (zero) e o último tem índice igual ao número de elementos da lista menos um: len(l) -1. A consulta de conteúdo na posição x aceita também valores negativos. O último elemento tem índice -1 (um), o primeiro elemento é -len(l) (tamanho de l) e 0 (zero).

No seguinte programa, você revisa alguns pontos sobre a manipulação de objetos. E aprende sobre um outro método útil dos objetos do tipo lista.


#Programa geral2.py
# linhas que começam com '#' nao sao lidas pelo interpretador
# Faça comentarios sempre que quiser

# nome mylist aponta para o objeto lista ['eu','tu','ele']
mylist = ['eu','tu','ele']

# imprimir o tipo do objeto “mylist” na tela
print type(mylist)

# imprimir os dados de mylist
print mylist

# imprimir que mylist.pop eh um metodo ou invocar o seu dado se for um atributo
mylist.pop

# '\n' é uma string especial para o print.
# Ela funciona como uma comando para pular uma linha
# Pule uma linha e imprima sua documentação:
print '\n', mylist.pop.__doc__

# aqui voce deve rodar o programa, F5 ou “run”, salve. e continue depois.

# ok, mylist.pop é uma função. Chame ele como uma função.
mylist.pop()

# ver o que aconteceu com o objeto mylist
print mylist


Você deverá ver algo parecido com isso:
<type 'list'>
['eu','tu','ele']

L.pop([index]) -> item -- remove and return item at index (default last)
['eu','tu']


2.3 Explorando os Recursos da Linguagem


2.3.1 Mais sobre Estruturas de Dados

Nesta seção, ainda mais do que nas outras, é recomendado que os comandos aqui expostos sejam testados escrevendo-se diretamente no interpretador, sem copiar e colar.

O Apêndice 7.1 detalha o uso de Dicionários. Algum dia, veja também o Dive Into Python para o uso de tuplas, outro tipo de estrutura de dados. Listas podem conter listas e também outros tipos de objetos.

Na verdade, qualquer objeto pode ser um elemento de uma lista. Vejamos um exemplo:


>>> print range.__doc__
range([start,] stop[, step]) -> list of integers

Return a list containing an arithmetic progression of integers.
range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
When step is given, it specifies the increment (or decrement).
For example, range(4) returns [0, 1, 2, 3].  The end point is omitted!
These are exactly the valid indices for a list of 4 elements.
>>> range( 7 )
[0, 1, 2, 3, 4, 5, 6]
>>>l = range( 5 ) # l é a lista retornada por range( 5 )
>>> l
[0, 1, 2, 3, 4]
>>> l[ 1 ] = [ 'a', 's', 'd', 'f' ] # O elemento de índice 1 é a lista ['a','s','d','f']
>>> l
[0, ['a', 's', 'd', 'f'], 2, 3, 4]
>>> l[ 1 ][ 2 ] = [ 'kramba', 'nossa', 'dahora' ] ## O elemento de índice dois do elemento de índice um da lista l
>>> l
[0, ['a', 's', ['kramba', 'nossa', 'dahora'], 'f'], 2, 3, 4]
>>> l[ 3 ] = dir ## Elemento de ìndice três é a função dir do Python
>>> l
[0, ['a', 's', ['kramba', 'nossa', 'dahora'], 'f'], 2, <built-in function dir>, 4]
>>> l[3](l) ## Como o l[3] == dir , l[3](l) equivale a dir(l), que retorna uma lista de membros de l
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__str__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
>>> l[1].append(l[3](l)[0]) ## Utilizando o método append da lista l[1] para adicionar l[3](l)[0] ao final de l[1]
>>> l
[0, ['a', 's', ['kramba', 'nossa', 'dahora'], 'f', '__add__'], 2, <built-in function dir>, 4]
>>> l[-1]={'eu':'nos','tu':'vos','ele':'eles', 'bicicleta':'capote'} # O último elemento de l é um Dicionário
>>> l
[0, ['a', 's', ['kct', 'nossa', 'dahora'], 'f', '__add__'], 2, <built-in function dir>, {'eu': 'nos', 'bicicleta': 'capote', 'tu': 'vos', 'ele': 'eles'}]
>>> l[-1]['tu']; l[-1]['bicicleta'] ## comandos diferentes podem ser dispotos em mesma linha por ';'
'vos'
'capote'


2.3.2Comparadores e Estrutura de Decisão


OBS: objetos do tipo string são indexados como listas

And, or | Igual, diferente, maior que, menor que, menor ou igual que, maior ou igual que: ==, !=, >, <, <=, >=


>>> 3 == 3 ## 3 é igual a 3? True
True
>>> 3 != 3 ## 3 é diferente de 3? False
False
>>> 3 == range(4)[-1] ## 3 é igual ao último elemento da lista retornada por range(4)??
True
>>> 3 == range(4) ## 3 é igual à lista retornada por range(4)
False
>>> 23 == 24 or 'a'=='abcd'[0] ## Operador 'ou' que retorna 'True' se qualquer uma das proposições forem 'True'
True
>>> 23 == 24 and 'a'=='abcd'[0] ## Operador 'e' que retorna 'True' somente se as duas proposições forem 'True'
False
>>> 23 < 24 and 'a'<'abcd' ## Operador comparativo 'menor que'
True
>>> 23 <= 24 and not 'a'>='abcd'v ## Operador comparativo 'menor/maior igual que' e o not, que retorna 'True' se receber 'False' e vice-versa.
True


A palavra-chave 'if' introduz uma estrutura de decisão. Esta executa o bloco indentado que segue à declaração se a expressão após o 'if' retornar True. Por exemplo:

if expressão_que_retorna_True_ou_False:
   bloco de código que executa se True, linha 1
   bloco de código que executa se True, linha 2
   bloco de código que executa se True, linha 3
   .........
else:
   bloco de código que executa se False, linha 1
   bloco de código que executa se False, linha 2
   bloco de código que executa se False, linha 3
   .........


Quando há a palavra-chave 'else' e um bloco de código subordinado a ela, este bloco é executado se a expressão após o 'if' retornar False.Como pode-se obsevar pelo esquema, a indentação é feita a partir de espaços ou tabs (mas não se deve misturá-los!). E todo bloco indentado possui o mesmo número de espaços (ou tabs) do começo da linha.

Condicional if - Estrutura de Decisão

>>> if 23 <= 24 and not 'a'>='abcd': ## Palavra-chave if, que executa o bloco seguinte (indentado) se obter 'True'.
...    print 'Eh verdade!'
...
Eh verdade!
>>> if 23 == 24:
...    print 'Eh verdade!'
...
>>> if 23 == 24:
...    print 'Eh verdade!'
... else: ## O bloco de código que segue ao else é executado se if receber False.!
...    print 'Nao eh verdade!'
...
Nao eh verdade!
>>> if not 23 == 24: ## o not, que retorna 'True' se receber 'False' e vice-versa.
...    print 'Eh nao verdade!'
... else:
...    print 'Eh nao mentira'
...
Eh nao verdade!
>>> if not 23 < 24:
...    print 'Eh nao verdade!'
... else:
...    print 'Eh nao mentira'
...
Eh nao mentira
>>> if 1: ## Inteiros não zero são entendidos como True
...   print 'tttttt'
...
tttttt
>>> if 0: ## Zero é entendido como False
...   print 'tttttt'
...
>>> if 8:
...   print 'tttttt'
...
'tttttt'
>>> if 'quarquercoisa': ## Qualquer string não vazia é True
...   print 'quarquer coisa eh True'
...
quarquer coisa eh True
>>> if '': ## Uma String vazia é entendida como False
...   print 'quarquer coisa eh True'
... else:
...   print 'nada nao eh True'
...
nada nao eh True
>>> if []: ## Uma Lista vazia é entendida como False
...   print 'quarquer coisa eh True'
... else:
...   print 'nada nao eh True'
...
nada nao eh True
>>> if [0]: ## Uma Lista não vazia é entendida como True.
...   print 'quarquer coisa eh True'
... else:
...   print 'coisa que contem zero nao eh True'
...
quarquer coisa eh True
>>> if [False]: ## Uma Lista não vazia é entendida como True.
...   print 'quarquer coisa eh True'
... else:
...   print 'coisa que contem False nao eh True'
...
quarquer coisa eh True
>>> if [[]]: ## Uma Lista não vazia é entendida como True.
...   print 'quarquer coisa eh True, teoria dos conjuntos'
... else:
...   print 'coisa que contem coisa que contem nada nao eh True'
...
quarquer coisa eh True
>>> if [0][0]: ## o elemento de índice 0 da lista [0] é 0 == False.
...  print 'huhuh'
... else:
...  print 'iiiiiiiiiii'
...
iiiiiiiiiii

2.3.3Laços de Repetição

Laços de repetição são blocos de código executados múltiplas vezes e condicionadas a uma expressão que define o ponto de término da repetição.

No Python, temos o 'while' e o 'for'. Não se esqueça do que vimos sobre listas e objetos.

>>> for i in 'abcde': ## A palavra-chave 'for' itera sobre cada elemento de um conjunto
...   print 'letra', i
...
letra a
letra b
letra c
letra d
letra e
>>> for meu_valor_do_loop in range(3):
...   print meu_valor_do_loop
...
0
1
2
>>> y = [0, [0, 1], 2.734, dir, {'eu': 'vc', 4: 5}]
>>> for i in y:
...    print i, type(i)
...
0 <type 'int'>
[0, 1] <type 'list'>
2.734 <type 'float'>
<built-in function dir> <type 'builtin_function_or_method'>
{'eu': 'vc', 4: 5} <type 'dict'>
>>> for i in y:
...    print 'mais um, mais um'
...
mais um, mais um
mais um, mais um
mais um, mais um
mais um, mais um
mais um, mais um
>>> n=0
>>> while n <= 4: ## 'while' repete o bloco indentado até que a expressão que a segue retorne False
...   print n, 'Eh menor ou igual a 4, soma 1'
...   n +=1
...
0 Eh menor ou igual a 4, soma 1
1 Eh menor ou igual a 4, soma 1
2 Eh menor ou igual a 4, soma 1
3 Eh menor ou igual a 4, soma 1
4 Eh menor ou igual a 4, soma 1
>>> while 1:
...   print '1 eh sempre True, e a gente fica aqui pra sempre, aperte ctrl+C'
1 eh sempre True, e a gente fica aqui pra sempre, aperte ctrl+C
1 eh sempre True, e a gente fica aqui pra sempre, aperte ctrl+C
1 eh sempre True, e a gente fica aqui pra sempre, aperte ctrl+C
1 eh sempre True, e a gente fica aqui pra sempre, aperte ctrl+C
1 eh sempre True, e a gente fica aqui pra sempre, aperte ctrl+C
.............


2.3.4Interpretador versus Compilador

Existem diversos comprometimentos da velocidade de desenvolvimento quanto ao uso de um interpretador ou um compilador. Rodar um programa através de um interpretador faz ele ficar relativamente lento, mas pode demorar menos para rodá-lo invocando o interpretador do que o tempo total requerido para compilar e rodar. Isso é especialmente importante na prototipagem e testes já que o ciclo editar - interpretar - arrumar(editar) é comumente mais curto do que um ciclo editar - compilar - rodar - arrumar(editar), principalmente quando o programa tem um processo de compilação muito custoso.
Interpretar um código é mais lento do que rodar o código compilado. Isto porque o interpretador precisa analisar cada expressão do programa sempre que ele é executado. Só depois que uma expressão escrita em Python é analisada o hadware sabe o que fazer. Ao passo que uma "interpretação" de um cógido compilado é feita diretamente em nível de hardware. Esta análise em nível de software em cada execução (run-time) é conhecido como “overhead interpretativo”.

3 – Fazendo Sons: Biblioteca de Módulos Padrão do Python

(corrigir a gosto)
A instalação padrão do Python inclui uma porção de código que pode ser usado livremente para escrever o que você desejar. Estes códigos, contidos em árvores de pastas e arquivos, são chamados de módulos e vc pode acessá-los. Existem módulos padrões para diversas tarefas. Existem também incontáveis módulos não padrões que podem ser baixados livremente na internet e costumam ser bem pequenos. Veremos alguns deles depois. Por hora, nesta unidade, iremos agora utilizar módulos padrão relacionados à manipulação de áudio. Primeiro, vamos ler e tocar um arquivo wav.

Para importar as biblitecas e utilizar suas funcionalidades dentro de um programa, temos 3 opções, que influenciarão principalmente na escrita do código:

1-
import nomedomodulo


Neste caso você importa todos os objetos do módulo e pode acessá-los com o prefixo:
nomedomodulo.objeto_do_modulo


2-

import nomedomodulo as N


Se você importar o módulo desta forma, mudará o prefixo necessário para acessar os objetos dele de:

nomedomodulo.objeto_do_modulo


para:

N.objeto_do_modulo


Uso sempre esta forma nos meus scripts pois diminiu o tamanho do código que tenho que escrever e ler, mas me indica claramente de onde vem cada objeto.

3-
from nomedomodulo import *


Desta forma você importa todos os objetos para o "namespace" chamado "main". Isso quer dizer que acessará os objetos do módulo como se fossem objetos quaisquer do python, sem o uso do prefixo:

nomedomodulo


Mas diretamente, assim:

objeto_do_modulo


CUIDADO! Isso deixa seu código muito ruim de ser entendido por outras pessoas, elas não saberão de que módulo vem cada objeto, ou mesmo se é do python "main" mesmo. Você mesmo pode acabar se confundindo! Não recomendado.

Usaremos neste tutorial a primeira forma para importar modulos (das três aqui listadas), assim você saberá sempre de onde vem cada objeto.

Vamos fazer alguns programas para a manipulação sonora, utilizando módulos da biblioteca padrão do Python:


#Programa sombibstd1.py
## Este programa toca um som em plataformas Linux. Para tocar no Windows, use
## o modulo ‘winsound’ em http://docs.python.org/lib/module-winsound.html ao inves
## do ‘ossaudiodev’ e escolha um arquivo wav do seu sistema
## Nao tenho certeza de que a linha 
## "dsp.setparameters(ossaudiodev.AFMT_S16_NE, nc, fr)" rodara num Mac
## Caso alguém teste este Codigo no Mac, me conte se funcionou
## No Linux e' certeza :-)

#Importando as bibliotecas
import wave, ossaudiodev

# Utilizando o objeto open do modulo wave estamos
# importando um arquivo de som com a opção ‘rb’ para leitura.
# Voce pode substituir este arquivo 'ok.wav' por qualquer outro .wav que vc tiver e quiser
mysound = wave.open('/usr/lib/openoffice/share/gallery/sounds/ok.wav' , 'rb')

# Atraves de metodos do objeto mysound, criado com o objeto 'wave.open',
# extraimos as informacoes do arquivo de som:

# 1- taxa de amostragem
fr = mysound.getframerate()
# 2- Numero de canais
nc = mysound.getnchannels()
# 3- Numero de amostras
nf = mysound.getnframes()

# lendo o arquivo, e guardando ele no objeto 'data'
# Isto e, lendo todas as amostras do objeto 'mysound'
data = mysound.readframes(nf)
mysound.close()

# abrindo uma conexao com a placa de som, para escrita
dsp = ossaudiodev.open('/dev/dsp','w')


# configurando a conexao com a placa de som de acordo com o arquivo que sera tocado
# Isto e, formato AFTM_S16_NE, com o numero de canais 'nc', e taxa de amostragem 'fr'
dsp.setparameters(ossaudiodev.AFMT_S16_NE, nc, fr)

# Tocando:
# Escrever no arquivo /dev/dsp quer dizer 'toca aih'
# Ouvi dizer que se poder tocar o kernel linux mandando ele para o /dev/dsp
## Deve ser puramente ruidoso, mas o procedimento e' ilustrativo
## Se alguem souber como se faz isso, coloca aqui ou me manda por email :-)
dsp.write(data)

#fechando a conexao do Python com a placa de som.
dsp.close()


Ótimo. Salvemos este arquivo, ele nos será útil. Faremos dele um módulo (isso mesmo) para tocar arquivos wav ou sons sintetizados. Sobre os módulos aqui utilizados, o módulo
wave

manipula arquivos wav para leitura e escrita. O
ossaudiodev

permite acesso à interface OSS (Open Sound System). A interface padrão no Linux é a ALSA, e a ALSA é compatível com o OSS, aliás, esta é uma das principais metas do projeto ALSA.

Neste ponto, estamos já fazendo coisas não muito triviais, quaisquer dúvidas, faça contato com os autores, e/ou procure na internet sobre os tópicos que estão lhe causando incertezas.

Façamos agora um arquivo .wav de 2 segundos com uma sinóide à 200 Herz.


#Programa sombibstd2.py

#importando as bibliotecas
import wave, struct, math

# Especificando a taxa de amostragem
fr = 44100 ##frame rate
# Especificando a o numero de amostras, 88200 sao 2 segundos na taxa de amostragem especificada
nf = 88200 ## number of frames
# Especificando a nossa frequencia
freq = 200
#    Queremos que o som tenha 2 segundos, um jeito facil de fazer isso neste programa e'
#    chamar a funcao range(x) com o numero de amostras que queremos (fr).
#    Isso nos da' uma lista de valores de zero ate o valor de x menos 1
ndeamostras=range( fr )

# Fazendo a senoide. Aqui temos alguns procedimentos envolvidos:
# 1) Um ciclo se completa a cada 2 * pi, representado aqui como 2.0 * math.pi
# 2) Multiplicar os valores (em radianos) para o qual calculamos nosso
#    oscilante coseno por cada um dos valores que esta' no objeto 'ndeamostras'
#    enquanto o dividimos pela nossa taxa de amostragem.
#    Isso nos dara' os dois segundos de senoide que queremos, mas ainda a 1 Herz
# 3) Queremos que a senoide seja de 200 Herz. Ou seja, complete 200 ciclos em cada segundo
#    Portanto basta que multipliquemos os valores para os quais estamos calculando
#    O cosseno por 200. Sao dois segundo de senoide a 200 Herz, lembra?!
# 4) Como resultado temos uma lista 'sig' dos valores da senoide de 200 Hz numa
#    taxa de amostragem de 44100 Hz, por 2 segundos.
#    Caso isso nao esteja claro, pegue um papel e rabisque um esquema da
#    Senoide oscilando, e de seus valores no tempo.
# 5) A sintaxe do Python pretende ser quase auto explicativa. E o e' depois de um certo uso.
#    Duvidas: consulte a intenet ou escreva para os autores.
sig = [ math.cos( 2.0 * math.pi * freq * x / fr ) for x in ndeamostras ]

# Ja que queremos gravar este arquivo, vamos normaliza-lo. Na verdade isso nao e'
# preciso neste caso, mas vamos utilizar esta pratica em outros exemplos, portanto
# vamos explicita-la agora. Pegue os limites superiores e inferiores da lista da senoide:
min_sig = min( sig )
max_sig = max( sig )

# Ache o âmbito e assegure que sera um float multiplicando-o por outro float 2.0
# 2.0 nao 1.0 pois um sinal de audio tipicamente oscila em torno de um zero
# Entao o valor maximo encontrado sera somente metade do ambito
ambit = 2.0 * max(max_sig, -min_sig)

# Com o ambito, podemos achar a escala para a resolucao de 16 bits, isto e', 2 elevado a 16:
scale = ( 2**16 - 1 )/( ambit )

# Escale o sinal para inteiros em 16bits
sig_16bit = [ int(scale*x) for x in sig ]

# Faça um objeto de escrita em arquivo wav
sound = wave.open('sinusoid.wav','w')

# Parametrize seu arquivo de escrita de som
# Mono, isto e', um so' canal
sound.setnchannels(1)
# Taxa de amostragem que estamos utilizando
sound.setframerate(fr)
# Numero de bytes do arquivo, estamos pensando em 16 bits, 2 bytes, certo?!
sound.setsampwidth(2)

# Escrevendo o sinal:
# Para isso, precisamos tornar cada um dos valores de sig_16bit
# uma variavel c chamada “short signed”
# na bibliteca padrao do Python, usamos o modulo struct para isso.

# Para cada valor da lista sig_16bit, chamada, neste contexto de 'i'
for i in sig_16bit:
	# Faca do 'i' uma variavel c “short signed” e escreva no arquivo
	sound.writeframes(struct.pack('h',i))


Pronto! Rode e ouça a senóide que produzimos. Ele deve estar na mesma pasta em que você salvou o script.

O módulo struct é a nossa ferramenta (da biblioteca padrão) para converter valores do Python para variáveis C (representada no python como strings). Ela é comumente utilizada para escrever um arquivo de som ou tocá-lo diretamente, pois os arquivos de som PCM comuns (sem compactação) sao constituidos de uma sequencia de valores no formato de variáveis C. O módulo math é a nossa ferramenta (da biblioteca padrão) para tornar operações matemáticas tradicionais mais fáceis e imediatas. É uma ferramenta muito útil, por isso vale a pena ler suas funções no manual do Python. Muito mais rápido e cheio de funcionalidades para operações matemáticas e manipulações de vetores é a biblioteca Numpy, que veremos na parte 5 deste texto.

Agora vamos fazer estes princípios trabalhados funcionarem juntos. Para tal, vamos aproveitar e mostrar como se faz um programa (ou script) se tornar um modulo para tocar e escrever os sons que criarmos.

Programa sombibstd1.py e sombibstd2.py juntos como um módulo


# Programa sombibstd3.py

import wave, struct, ossaudiodev

# Definindo uma funcao. Esta pode ser utilizada pelo nome,
# Ou, quando importarmos este script como um modulo,
# Pode ser chamada como um metodo do modulo.
# Ao definir uma funcao, voce deve especificar quais parametros ela precisa receber
# para ser inicializada, no caso:
# sig (sinal), fr (taxa de amostragem)
# nc (numero de canais), nf (numero de amostras)
# Poderiamos tambem incluir a resolucao de cada amostra,
# Mas resolvemos fazer sons somente com 16 bits.
# Quem estiver ja sendo especificado, e' opcional quando a funcao for chamada
# e recebe o valor que esta' na sua criacao (com o '=')
# Quem nao estiver sendo especificado na criacao, deve ser especificado quando a funcao for chamada
# ou causara' erro e a interrupcao do programa
def player(sig, nc=1, fr=44100, nf=88200):
	# se o objeto sig for uma lista:
	if type(sig) == list:
		# valores maximos e minimos da lista
		min_sig = min(sig)
		max_sig = max(sig)
		
		# ambito
		ambit = 2.0 * max(max_sig, -min_sig)
		

		# Achando a escala dos valores de sig para 16 bits
		# Caso o ambito seja menor do que 2
		# Que e' o maximo em medida RMS ou Peak para audio digital em computadores
		# Preservar a proporcao de aplitude
		if ambit < 2:
			scale = ( 2**16 - 1 ) * (ambit / 2)
		
		# Caso contrario, ou seja, o ambito seja menor do que 2
		# Fazer a escala para normalizar
		# Normalizar e' fazer com que o sinal varie em amplitude por
		# todo o ambito permitido
		else:
			scale = ( 2**16 - 1 ) / ( ambit )

		# sinal em 16 bits
		sig_16bit = [ int(scale*x) for x in sig ]

		# O sinal sera uma string (para o python) de variaveis C short signed por hora vazia
		data = ''

		# Escrever o sinal no objeto data, x += y e' o mesmo que x = x + y
		# O que, para strings no Python, e' o mesmo que adicionar y no final
		for i in range( len(sig_16bit) ):
			data += struct.pack('h',sig_16bit[i])
	else:
		# Caso o sinal 'sig' nao seja uma lista, vamos aceita-lo como nosso 'data'
		# na sorte de que seja uma string de variaveis short-signed C
		data=sig

        # Aqui tentamos tocar o som, pois /dev/dsp pode estar ocupada
	# Isso causa um erro e o encerramento do programa
	# Mas com esse try, o interpretador esta instruido a nao interromper a execussao
	# No caso de erro, mas sim pular para o bloco que segue o 'except'
	# Isso e' conhecido como Error Handling
	try:
		# Abrindo o /dev/dsp para escrita e ajeitando seus parametros
		# (veja o script anterior a este)
		dsp = ossaudiodev.open('/dev/dsp','w')
		dsp.setparameters(ossaudiodev.AFMT_S16_NE, nc, fr)
		# Escrevendo na /dev/dsp para tocar o som
		dsp.write(data)
		# Fechando a comundicacao com /dev/dsp
		dsp.close()
	except IOError:
		print "ok, /dev/dsp/ ocupado, nao tocamos, mas o arquivo sera escrito"

	# Escrevendo o arquivo de som (veja o script anterior a este)
	sound = wave.open('qualquersom.wav','w')
	sound.setnchannels(nc)
	sound.setframerate(fr)
	sound.setsampwidth(2)
	sound.writeframes(data)

# vai aqui um truque. A variavel global __name__ so' sera' __main__ se rodarmos o 
# programa noo como um modulo, mas como o programa principal. Assim podemos
# testar nosso script sem ter que fazer outro script para importa'-lo como um modulo.

# Se '__name__' for a string '__main__'
if __name__ == '__main__':
	# Abra um som e o rode, veja os 2 scripts anteriores a este
	sound = wave.open('/usr/lib/openoffice/share/gallery/sounds/ok.wav' , 'rb')
	fr = sound.getframerate()
	nc = sound.getnchannels()
	nf = sound.getnframes()
	data = sound.readframes(nf)
	sound.close()
	# testando a nossa funcao 'player' definida acima
	player(data, nc, fr, nf)


Rode-o e verifique se está tudo certo. Nossos próximos programas o utilizarão como um módulo.


Agora, faça o seguinte script no mesmo diretório em que salvou o sombibstd3.py.

Ele vai importar o programa sombibstd3.py e utilizará suas funcionalidades.


# Programa sombibstd4.py (Usando o programa sombibstd3.py como um modulo tocador)

# Importando o modulo math e o script/mini-modulo sombibstd
import math, sombibstd3

# Fazendo uma senoide de 123 Herz
fr = 44100 ##frame rate
nf = 88100 ## number of frames
sig = [ math.sin( 2.0*math.pi*123*x/44100 ) for x in range(nf) ]

# Invocando a função player do script importado sombibstd3.py
sombibstd3.player(sig, 1, fr, nf)


Salve, rode e note que ele toca e escreve a senoóide utilizando a função 'player' do arquivo sombibstd3.py!
Exatamente o que está acontecendo? Comecemos pelo programa sombibstd3.py. Lá, estamos fazendo uma senóide utilizando o módulo math e mandando o sinal-senóide (e os parâmetros: número de canais, taxa de amostragem e número de amostras) para a função player do nosso módulo programa sombibstd3.py. O programa sombibstd3.py toma conta de tudo a partir de então. Ele roda a função player porque ela foi invocada no programa sombibstd4.py. Então ele escala o sinal para a resolução e âmbito de 16 bits, e escreve uma estrutura C “short signaled” dele. Configura o OSS (ou ALSA) e escreve o sinal no dispositivo de som para que possamos ouvi-lo. Após isso, ele escreve nosso arquivo .wav. Observe que ele checa seu atributo name que não é main, então ele não executa o bloco de código que segue.

Esta modularidade é muito útil em um processo de desenvolvimento de programas. Agora vamos demonstrar como podemos progredir com algumas idéias sem ter que nos preocupar com aquelas coisas todas de escalonamento, codificação e abertura de dispositivos.

Vamos fazer um pequeno programa para sintetizar um som com alguns parciais!


# Programa sombibstd5.py

import math, sombibstd3

n_harm = input('Entre com o numero de parciais, um inteiro, por obsequio: ') ##


sig = [] ## lista nula para o sinal
fr = 44100 ## taxa de amostragem
n = 1 ## numero do parcial, primeiro o primeiro, 1 ;-)

## loop para repetir o bloco de codigo enquanto nao acabar o numero de harmonicos
while n <= n_harm: 
	# Imprimindo o cabecalho de entradas para o parcial n
	print '\n\ndata of partial number', n
	freq = input('frequency(Hz): ') ## Pedindo especificacao de frequencia
	amp = input('amplitude (RMS): ') ## Amplitude
	dur = input('duration(ms): ') ## Duracao

	# Assegurando que o parcial comecara no inicio de um ciclo para evitar clicks muito fortes
	# Nao funciona muito bem, mas ajuda
	# Se voce quiser um som sem clicks, faca fades: rampas de amplitude 
	# para a entrada e saida do parcial
	dur = dur - dur%(1.0/freq)

	# Conseguir um inteiro pq a funcao range precisa de um inteiro
	# E usaremos a funcao range para termos o numero de amostras do sinal
	nf = int(dur*44.1) 

	# Se o numero de amostras do parcial for menor ou igual ao numero de amostras
	# que o sinal ja' possui, somar os valores ao sinal
	if nf <= len(sig):
		for i in range( nf ):
			sig[i] += math.sin( 2.0*math.pi*freq*i/fr )

	# Se o numero de amostras do parcial for maior que o numero de amostras
	# que o sinal ja' possui, somar os valores ao sinal e, depois
	# adicionar valores ao final da lista
	if nf > len(sig):
		for i in range( len(sig) ):
			sig[i] += math.sin( 2.0*math.pi*freq*i/fr )
		
		cut = len(sig)
		sig += [ amp*math.sin( 2.0*math.pi*freq*x/fr ) for x in range( cut, nf, 1 ) ]

	# somando + 1 na referencia de numero de parciais
	# e terminado o bloco de codigo para voltar ao inicio do bloco ou sair do loop se 
	# n > n_harm
	n+=1

# Chamando a funcao player do sombibstd3 para tocar e gravar o som.
sombinstd3.player(sig, 1, fr, nf)



4 – O Módulo PySndObj

(corrigir a gosto, discorrer sobre o código com #, escrever arquivos de som com o PySndObj)
O módulo PySndObj? é uma tradução para o Python da biblioteca SndObj?, uma biblioteca C++ para síntese e processamento de som desenvolvida em código aberto feita principalmente pelo brasileiro Victor Lazzarini. Sobre esta biblioteca, pode-se encontrar explicações de uso tanto no site http://music.nuim.ie//musictec/SndObj/ quanto em um artigo escrito apresentado recentemente em Berlim que trata especialmente sobre este modulo: http://www.kgw.tu-berlin.de/~lac2007/papers/lac07_lazzarini.pdf
A esta altura, o leitor deve estar ferramentado suficientemente para fazer uso do manual da biblioteca para entender o que se passa. Estes exemplos intentam auxiliar com novos exemplos. Trataremos de principalmente de síntese FM e de ruídos.

Senoide

#Simple SIN

from sndobj import *
import time

tab = HarmTable(2500, SINE, 1)

snd = Oscili(tab, 388, 3000)
outp = SndRTIO(1, SND_OUTPUT)

outp.SetOutput(1, snd)

thread = SndThread()
thread.AddObj(snd)
thread.AddObj(outp, SNDIO_OUT)

thread.ProcOn()
time.sleep(30)
thread.ProcOff()


Senóide Modulante

#Simple Modulating SIN 

from sndobj import *
import time

tab = HarmTable(2500, SINE, 1)
mod = Oscili(tab, 0.5, 40) ##
snd = Oscili(tab, 388, 3000, mod##) 
outp = SndRTIO(1, SND_OUTPUT)

outp.SetOutput(1, snd)

thread = SndThread()
thread.AddObj(mod) ##
thread.AddObj(snd)
thread.AddObj(outp, SNDIO_OUT)

thread.ProcOn()
time.sleep(30)
thread.ProcOff()


FM

#Simple FM

from sndobj import *
import time

tab = HarmTable(2500, SINE, 1)
mod = Oscili(tab, 200##, 40)
snd = Oscili(tab, 388, 3000, mod) 
outp = SndRTIO(1, SND_OUTPUT)

outp.SetOutput(1, snd)

thread = SndThread()
thread.AddObj(mod)
thread.AddObj(snd)
thread.AddObj(outp, SNDIO_OUT)

thread.ProcOn()
time.sleep(30)
thread.ProcOff()


FM com frequencia modulatória variável

#Simple FM with varying modulation frequency

from sndobj import *
import time

tab = HarmTable(2500, SINE, 1)
modfreq = Oscili(tab, 0.03, 200) ##
mod = Oscili(tab, 200, 40, mod2##)
snd = Oscili(tab, 388, 3000, mod) 
outp = SndRTIO(1, SND_OUTPUT)

outp.SetOutput(1, snd)

thread = SndThread()
thread.AddObj(modfreq) ##
thread.AddObj(mod)
thread.AddObj(snd)
thread.AddObj(outp, SNDIO_OUT)

thread.ProcOn()
time.sleep(30)
thread.ProcOff()


FM com frequencia modulatória e índice variáveis

#Simple FM with varying modulation frequency and index

from sndobj import *
import time

tab = HarmTable(2500, SINE, 1)
modind = Oscili(tab, 0.3, 40) ##
modfreq = Oscili(tab, 0.03, 200)
mod = Oscili(tab, 200, 40, mod2, mod3##)
snd = Oscili(tab, 388, 3000, mod) 
outp = SndRTIO(1, SND_OUTPUT)

outp.SetOutput(1, snd)

thread = SndThread()
thread.AddObj(modind) ##
thread.AddObj(modfreq)
thread.AddObj(mod)
thread.AddObj(snd)
thread.AddObj(outp, SNDIO_OUT)

thread.ProcOn()
time.sleep(30)
thread.ProcOff()


Outros Programas sem especificações

# What?
#Extra program 1
import sndobj, time

tab = sndobj.HarmTable()
noise = sndobj.Randh(100,1000)
osc = sndobj.Oscili(tab, 440, 440, )
outp = sndobj.SndRTIO(2, sndobj.SND_OUTPUT)

outp.SetOutput(1, osc)
outp.SetOutput(2, noise)

thread = sndobj.SndThread()
thread.AddObj(osc)
thread.AddObj(noise)
thread.AddObj(outp, sndobj.SNDIO_OUT)

thread.ProcOn()
time.sleep(30)
thread.ProcOff()


# What?
# Extra Program 3
import sndobj, time

tab = sndobj.HarmTable()
noise = sndobj.Randh(15##, 1000 ##)
osc = sndobj.Oscili(tab, 40 ##, 4400, noise)
outp = sndobj.SndRTIO(2, sndobj.SND_OUTPUT)

outp.SetOutput(1, osc)
outp.SetOutput(2, noise)

thread = sndobj.SndThread()
thread.AddObj(osc)
thread.AddObj(noise)
thread.AddObj(outp, sndobj.SNDIO_OUT)

thread.ProcOn()
time.sleep(30)
thread.ProcOff()

# What
# Extra Program 4
import sndobj, time

tab = sndobj.HarmTable()
mod = sndobj.Oscili(tab, 0.03, 60) ##
noise = sndobj.Randh(15, 3500, mod ##, mod ##)
osc = sndobj.Oscili(tab, 40, 4400, noise ##, noise ##)
outp = sndobj.SndRTIO(2, sndobj.SND_OUTPUT)

outp.SetOutput(1, osc)
outp.SetOutput(2, osc##)

thread = sndobj.SndThread()
#thread.AddObj(mod)
thread.AddObj(osc)
thread.AddObj(noise)
thread.AddObj(outp, sndobj.SNDIO_OUT)

thread.ProcOn()
time.sleep(30)
thread.ProcOff()

# What?
# Extra Program 5
import sndobj, time

tab = sndobj.HarmTable()
mod = sndobj.Oscili(tab, 0.03, 60)
noise = sndobj.Randh(15, 3500, mod, mod)
noise2 = sndobj.Randh(17, 3500, mod, mod)##
osc = sndobj.Oscili(tab, 40, 4400, noise, noise)
osc2 = sndobj.Oscili(tab, 40, 4400, noise2, noise2)##
outp = sndobj.SndRTIO(2, sndobj.SND_OUTPUT)

outp.SetOutput(1, osc)
outp.SetOutput(2, osc2)##

thread = sndobj.SndThread()
#thread.AddObj(mod)
thread.AddObj(osc)
thread.AddObj(osc2)## 
thread.AddObj(noise)
thread.AddObj(noise2)##
thread.AddObj(outp, sndobj.SNDIO_OUT)

thread.ProcOn()
time.sleep(30)
thread.ProcOff()


5 – Numpy

(aterar a gosto, fazer algumas lookup tables)
Para operações matemáticas e manipulação de vetores multidimensionais, o Python conta com o módulo NumPy?. Em verdade, existem três diferentes implementações do Numerical Python. A original, Numeric, que é razoavelmente estável e completa continua disponível, mas é considerada obsoleta. Uma mais nova, Numarray, apresenta-se como uma completa reescrita do Numerical Python. A mais recente NumPy? é uma mescla entre as duas que se baseia no código da Numeric e adiciona ferramentas do Numarray.
Alguns vê o NumPy? como uma boa e gratuita alternativa ao MATLAB, já que ambos são semelhantes em alguns sentidos: ambos são interpretados, e ambos permitem que o usuário escreva programas rápidos enquanto a maioria das operações trabalham em vetores ou matrizes e não em escalares. Para o caso do áudio, é importante notar que o NumPy?, e os pacotes do SciPy? (ferramentas matemáticas que utilizam os algoritmos de vetores multidimensionais do NumPy), possui objetos que permitem a fácil utilização da série e transformada de Fourier, Wavelets e processos estocásticos, assim como a capacidade de fazer espectrogramas e contrução avançada de gráficos.
O módulo Audiolab (era Pyaudiolab até recentemente) permite escrever e ler arquivos de som em diversos formatos (wav, flac, aiff, etc) diretamente de e para vetores do NumPy?. Veremos somente algumas funcionalidades do NumPy?, para informações sobre o Audiolab veja: http://www.ar.media.kyoto-u.ac.jp/members/david/softwares/audiolab/

Numpy em ação e suas particularidades

>>> import numpy as N
>>> x = N.array([1,2,3,4])
>>> type(x)
<type 'numpy.ndarray'>
>>> y = [1,2,3,4]
>>> y
[1, 2, 3, 4]
>>> x
array([1, 2, 3, 4])
>>> y+y
[1, 2, 3, 4, 1, 2, 3, 4]
>>> x+x
array([2, 4, 6, 8])
>>> 3*y
[1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]
>>> 3*x
array([ 3,  6,  9, 12]) ## to do this with the list x you would need to loop through each element.
>>> N.cos(x)
array([ 0.54030231, -0.41614684, -0.9899925 , -0.65364362])
>>> N.cos(y)
array([ 0.54030231, -0.41614684, -0.9899925 , -0.65364362])
>>> list(x)
[1, 2, 3, 4]
>>> N.array(y)
array([1, 2, 3, 4])
>>> import math as M
>>> y
[1, 2, 3, 4]
>>> M.cos(y)
>>> M.cos(y)
Traceback (most recent call last):
  File "<pyshell#39>", line 1, in ?
    M.cos(y)
TypeError: a float is required
>>> N.arange(4)
array([0, 1, 2, 3])
>>> N.arange(4, 100, 14.78)
array([  4.  ,  18.78,  33.56,  48.34,  63.12,  77.9 ,  92.68])
>>> z = N.zeros( (3,4,5) )
>>> z
array([[[ 0.,  0.,  0.,  0.,  0.],
        [ 0.,  0.,  0.,  0.,  0.],
        [ 0.,  0.,  0.,  0.,  0.],
        [ 0.,  0.,  0.,  0.,  0.]],

       [[ 0.,  0.,  0.,  0.,  0.],
        [ 0.,  0.,  0.,  0.,  0.],
        [ 0.,  0.,  0.,  0.,  0.],
        [ 0.,  0.,  0.,  0.,  0.]],

       [[ 0.,  0.,  0.,  0.,  0.],
        [ 0.,  0.,  0.,  0.,  0.],
        [ 0.,  0.,  0.,  0.,  0.],
        [ 0.,  0.,  0.,  0.,  0.]]])

>>> z[1]
array([[ 0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.]])
>>> z[1][1]
array([ 0.,  0.,  0.,  0.,  0.])
>>> z[1][1][1]
0.0
>>> z[0]
array([[ 0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.]])
>>> z[1]
array([[ 0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.]])
>>> z[1][1]
array([ 0.,  0.,  0.,  0.,  0.])
>>> z[1][1][1]
0.0



Construiremos agora algumas formas de ondas básicas utilizando o NumPy?.


#Basic Waveforms

import numpy as N
import sombibtd3 # ver parte 3 deste tutorial

freq = 100
period = 1.0/freq
samples = 44100*period

square_wav = N.hstack(( N.ones(samples/2), -N.ones(samples/2) ))
saw_wav = N.linspace(-1, 1, samples)
triangle_wav = N.hstack(( N.linspace(-1,1,samples/2), N.linspace(1,-1,samples/2) ))
sin_wav = N.sin( N.linspace(0, 2*N.pi, samples) )
noise_wave = N.random.uniform(-1,1,88200)

print len(square_wav)
program5.player( 'square', list(square_wav), 1, 44100, samples )
program5.player( 'saw', list(saw_wav), 1, 44100, samples )
program5.player( 'triangle', list(triangle_wav), 1, 44100, samples )
program5.player( 'sin', list(sin_wav), 1, 44100, samples )
program5.player( 'noise', list(noise_wave), 1, 44100, samples )




6 – Ctypes

(aterar a gosto, fazer algum programa que envolva áudio)
O módulo Ctypes é padrão no Python desde a versão 2.5 (estamos na 3.0). Ele permite a utilização de variáveis C e de estruturas de dados C. Vejamos como ele funciona:

>>> from ctypes.util import find_library
>>> from ctypes.util import find_library
>>> find_library('openal')
'libopenal.so.0'
>>> find_library('c')
'libc.so.6'


Funções externas podem ser acessadas como atributos de bibliotecas carregadas:

>>> c=CDLL('libc.so.6')
>>> c.printf
<_FuncPtr object at 0xb7d2babc>
>>> c.printf('tyty')
tyty4
>>> c.printf('tyty   ')
tyty   7
>>> c.printf('tyty   \n')
tyty
8


Encerre e inicie o Python novamente:

>>> from ctypes import *
>>> c_long
<class 'ctypes.c_long'>
>>> c_char
<class 'ctypes.c_char'>
>>> c_double
<class 'ctypes.c_double'>


Você pode definir estruturas C. Comparemos estruturas Python com estruturas C escritas com o Ctypes:

>>> class PyPoint:
...     def __init__(self, value1, value2):
...             self.x=value1
...             self.y=value2
...
>>> PyPoint(3,4)
<__main__.PyPoint instance at 0xb7d27a2c>
>>> point1=PyPoint(3,4)
>>> point1.x
3
>>> point1.y
4

>>> class CPoint(Structure):
...     _fields_ = (('x', c_long),('y',c_long))
...
>>> CPoint.x
<Field type=c_long, ofs=0, size=4>
>>> CPoint.y
<Field type=c_long, ofs=4, size=4>
>>> CPoint().x
0
>>> CPoint().y
0
>>> point2 = CPoint(5,6)
>>> point2.x
5
>>> point2.y
6
>>> fourp=CPoint*4
>>> myarr=fourp((1,2),(3,4),(5,6),(7,8))
>>> myarr[1].x
3
>>> for i in range(len(myarr)):
...     print 'point', i+1,  '= (', myarr[i].x , ',' , myarr[i].y,  ')'
...
point 1 ( 1 , 2 )
point 2 ( 3 , 4 )
point 3 ( 5 , 6 )
point 4 ( 7 , 8 )


Formatação de strings:

>>> for i in range(len(myarr)):
...     print 'point %i = ( %i, %i )' %(i+1, myarr[i].x , myarr[i].y)
...
point 1 = ( 1, 2 )
point 2 = ( 3, 4 )
point 3 = ( 5, 6 )
point 4 = ( 7, 8 )


Algumas convenções de tipagem:
d ou i Signed integer decimal. G Floating point format. (4) c Single character. s String.

Ponteiros:

>>> c=c_long(78)
>>> c
c_long(78)
>>> p=pointer(c)
>>> p
<ctypes.LP_c_long object at 0xb7d844ac>
>>> p.contents
c_long(78)
>>> q=p
>>> p[0]
78
>>> p[0]=66
>>> c
c_long(66)
>>> q[0]
66


Ponteiros necessitam de informações de estocagem. Se precisa somente apontar para um tipo, use POINTER:

>>> mystuff = pointer(c_long)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "/usr/lib/python2.4/site-packages/ctypes/__init__.py", line 282, in pointer
    return POINTER(type(inst))(inst)
  File "/usr/lib/python2.4/site-packages/ctypes/__init__.py", line 226, in POINTER
    {'_type_': cls})
TypeError: _type_ must have storage info
>>> mystuff = POINTER(c_long)
>>> mystuff
<class 'ctypes.LP_c_long'>
>>> mything = mystuff(c_long(23))
>>> mything
<ctypes.LP_c_long object at 0xb7d847c4>


POINTER(c_int), ctypes aceita uma lista de c_int:
>>> def Check(Structure):
...     _fields_ = (   ( 'x', POINTER(c_int) )   )
...
>>> Check.x=(c_int*4)(34, 56, 67, 78)
>>> for i in range(len(Check.x)):
...     print Check.x[i]
...
34
56
67


7 Apêndice (mais)


7.1 Dicionários


Dicionários são semelhantes às listas, mas a indexação se dá através qualquer objeto que não é um dicionário ou uma lista, chamado de 'Chave' ('Key'). Ou seja, cada elemento de um dicionário é constituído de um objeto 'Chave' ao qual está associado um 'Valor'. Cada elemento possui é a forma 'Chave'/'Valor'. Vejamos isso:

>>> dik={} # Todo dicionário é expressado através de um conteúdo entre chaves {Chave1:Valor1, Chave2:Valor2}
>>> dik['hash']=1 # Criando um elemento no dicionário dik cuja Chave é a string 'hash' e o Valor é o inteiro 1.
>>> dik # Como está o objeto dik
{'hash': 1}
>>> dik['thrash']=2
>>> dik
{'thrash': 2, 'hash': 1}
>>> dik['bash']='baba' # Criando um elemento no dicionário dik cuja Chave é a string 'bash' e o Valor é a string 'baba'.
>>> dik
{'thrash': 2, 'hash': 1, 'bash': 'baba'}
>>> dik['hash'] # Acessando o Valor da Chave 'hash'
1
>>> dir(dik) # Mostrar os membros do objeto dik.
['__class__', '__cmp__', '__contains__', '__delattr__', '__delitem__', '__doc__', '__eq__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__str__', 'clear', 'copy', 'fromkeys', 'get', 'has_key', 'items', 'iteritems', 'iterkeys', 'itervalues', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
>>> dik.pop # O que ele é?
<built-in method pop of dict object at 0xb7cf1604>
>>> dik.pop() #Chamando como um método
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: pop expected at least 1 arguments, got 0
>>> print dik.pop.__doc__
D.pop(k[,d]) -> v, remove specified key and return the corresponding value
If key is not found, d is returned if given, otherwise KeyError is raised
>>> dik.pop('bash') # Aplicando o que está no doc string do objeto dik.pop
'baba'
>>> dik
{'thrash': 2, 'hash': 1}

Última alteração: 26/08/2011 às 14:42, por: gk