61
AudioLazy DSP ( DSP (Digital Signal Processing Digital Signal Processing) expressivo e ) expressivo e em tempo real para o Python em tempo real para o Python Projeto open source (GPLv3) Projeto open source (GPLv3) http://pypi.python.org/pypi/audiolazy http://pypi.python.org/pypi/audiolazy Copyright (C) 2012-2013 Copyright (C) 2012-2013 Danilo de Jesus da Silva Bellini Danilo de Jesus da Silva Bellini danilo [dot] bellini [at] gmail [dot] com danilo [dot] bellini [at] gmail [dot] com

(2013-05-03) AudioLazy - Slides

Embed Size (px)

DESCRIPTION

Slides baseados na defesa de mestrado ocorrida no dia anterior, com algumas correções.

Citation preview

Page 1: (2013-05-03) AudioLazy - Slides

AudioLazy

DSP (DSP (Digital Signal ProcessingDigital Signal Processing) expressivo e ) expressivo e em tempo real para o Pythonem tempo real para o Python

Projeto open source (GPLv3)Projeto open source (GPLv3)

http://pypi.python.org/pypi/audiolazyhttp://pypi.python.org/pypi/audiolazy

Copyright (C) 2012-2013Copyright (C) 2012-2013Danilo de Jesus da Silva BelliniDanilo de Jesus da Silva Bellini

danilo [dot] bellini [at] gmail [dot] comdanilo [dot] bellini [at] gmail [dot] com

Page 2: (2013-05-03) AudioLazy - Slides

Parte 1

Aspectos geraisAspectos gerais

Page 3: (2013-05-03) AudioLazy - Slides

AudioLazy

● DSP (Digital Signal Processing) para áudio– Análise

● MIR (Music Information Retrieval)

– Síntese

– Processamento

● Expressividade de código– Facilita prototipação, simulação

● Tempo real (latência de 35ms é possível)– Possibilita uso em aplicações finais

● 100% Python– Multiplataforma

Page 4: (2013-05-03) AudioLazy - Slides

Justificativa

● Demanda e insatisfação com código existente– Sustainable Software for Audio and Music Research

● ISMIR 2012 (Tutorial)● DAFx 2012 (Tutorial)

– Software Carpentry

● Ausência de código fonte disponível– Algoritmo de Klapuri (2008)

● Base para trabalhos futuros

Page 5: (2013-05-03) AudioLazy - Slides

Linguagem

Critério MatLab Octave PureData Python NumPy

Amostras Sim Sim Depende Sim Sim

Blocos Possível Possível Sim Possível Possível

Álgebra linear Sim Sim Depende Possível Sim

DSP Sim Sim Sim Possível Sim

Tempo real Não Não Sim Possível Não

Heterogeneidade Não Não Não Sim Possível

Avaliação tardia Não Não Depende Sim Não

Funções de ordem superior Não Não Não Sim Não

Orientação a objetos Possível Possível Não Sim Sim

Linguagem de uso geral

Não Não Não Sim Sim

Documentação Sim Sim Sim Sim Sim

Licença Proprietária GNU GPLBSD

ModificadaPSFL BSD

Preço US$99.00 Gratuito Gratuito Gratuito Gratuito

Código fonte (Fechado) C++ C C, PythonC, Fortran,

Python

Page 6: (2013-05-03) AudioLazy - Slides

Outras linguagens

● CAS (Computer Algebra System)– Wolfram Mathematica

– SymPy

● C, C++, Java– Estáticas

– Imperativo

– S/ ênfase em ser expressivo

● DSL (Domain Specific Language)– FAUST

– CSound

– SuperCollider

– ChunK

● Pacotes– YAAFE

– MARLib

Page 7: (2013-05-03) AudioLazy - Slides

Resultados cobertura de código(Testes automatizados)

--------------- coverage: platform linux2, python 2.7.3-final-0 ---------------Name Stmts Miss Branch BrPart Cover Missing------------------------------------------------------------__init__ 44 1 18 4 92% 72lazy_analysis 124 3 62 4 96% 210, 258-259lazy_auditory 60 0 14 0 100% lazy_core 130 7 56 5 94% 93-94, 108, 151, 211, [...]lazy_filters 508 180 249 118 61% 53, 60, 81, 91, [...]lazy_io 123 41 38 23 60% 35-37, 58-72, 76, 80, [...]lazy_itertools 14 3 10 8 54% 61-64lazy_lpc 127 15 42 7 87% 120, 134-135, [...]lazy_math 59 1 28 0 99% 131lazy_midi 54 5 26 3 90% 70, 111, 150, 156, 158lazy_misc 208 28 138 30 83% 136, 201-202, 239, [...]lazy_poly 126 30 89 34 70% 90, 146-149, 160, [...]lazy_stream 144 2 56 4 97% 62, 606lazy_synth 241 50 118 48 73% 277-299, 319-323, [...]------------------------------------------------------------TOTAL 1962 366 944 288 77%

========================= 5501 passed in 23.59 seconds =========================

--------------- coverage: platform linux2, python 2.7.3-final-0 ---------------Name Stmts Miss Branch BrPart Cover Missing------------------------------------------------------------__init__ 44 1 18 4 92% 72lazy_analysis 124 3 62 4 96% 210, 258-259lazy_auditory 60 0 14 0 100% lazy_core 130 7 56 5 94% 93-94, 108, 151, 211, [...]lazy_filters 508 180 249 118 61% 53, 60, 81, 91, [...]lazy_io 123 41 38 23 60% 35-37, 58-72, 76, 80, [...]lazy_itertools 14 3 10 8 54% 61-64lazy_lpc 127 15 42 7 87% 120, 134-135, [...]lazy_math 59 1 28 0 99% 131lazy_midi 54 5 26 3 90% 70, 111, 150, 156, 158lazy_misc 208 28 138 30 83% 136, 201-202, 239, [...]lazy_poly 126 30 89 34 70% 90, 146-149, 160, [...]lazy_stream 144 2 56 4 97% 62, 606lazy_synth 241 50 118 48 73% 277-299, 319-323, [...]------------------------------------------------------------TOTAL 1962 366 944 288 77%

========================= 5501 passed in 23.59 seconds =========================

Mock

Page 8: (2013-05-03) AudioLazy - Slides

Testes com oráculos

● 80 c/ o scipy.signal.lfilter● 64 c/ o subpacote de otimização do SciPy● 2 c/ o NumPy

import pytestp = pytest.mark.parametrize

from scipy.signal import lfilterfrom audiolazy import ZFilter, almost_eq

class TestZFilterScipy(object):

@p("a", [[1.], [3.], [1., 3.], [15., -17.2], [-18., 9.8, 0., 14.3]]) @p("b", [[1.], [-1.], [1., 0., -1.], [1., 3.]]) @p("data", [range(5), range(5, 0, -1), [7, 22, -5], [8., 3., 15.]]) def test_lfilter(self, a, b, data): filt = ZFilter(b, a) # Cria um filtro com a AudioLazy expected = lfilter(b, a, data).tolist() # Aplica o filtro com o SciPy assert almost_eq(filt(data), expected) # Compara os resultados

import pytestp = pytest.mark.parametrize

from scipy.signal import lfilterfrom audiolazy import ZFilter, almost_eq

class TestZFilterScipy(object):

@p("a", [[1.], [3.], [1., 3.], [15., -17.2], [-18., 9.8, 0., 14.3]]) @p("b", [[1.], [-1.], [1., 0., -1.], [1., 3.]]) @p("data", [range(5), range(5, 0, -1), [7, 22, -5], [8., 3., 15.]]) def test_lfilter(self, a, b, data): filt = ZFilter(b, a) # Cria um filtro com a AudioLazy expected = lfilter(b, a, data).tolist() # Aplica o filtro com o SciPy assert almost_eq(filt(data), expected) # Compara os resultados

Page 9: (2013-05-03) AudioLazy - Slides

Comparação de números em ponto flutuante (IEEE 754)

● Valor absoluto (limiar “l”)

● Comparação pelo número de bits de mantissa (“t” bits de tolerância para “s” bits de mantissa)

● Implementado na audiolazy.lazy_misc– almost_eq_diff

– almost_eq

∣a−b∣≤l

∣a−b∣≤2(t − s−1)∣a+b∣

Page 10: (2013-05-03) AudioLazy - Slides

Parte 2

Síntese e processamento em tempo realSíntese e processamento em tempo real

Page 11: (2013-05-03) AudioLazy - Slides

“Hello world” em áudio

● Tocar uma senóide– Console interativo

– Script

from audiolazy import *

rate = 44100s, Hz = sHz(rate)th = AudioIO().play(sinusoid(440 * Hz), rate=rate)

from audiolazy import *

rate = 44100s, Hz = sHz(rate)th = AudioIO().play(sinusoid(440 * Hz), rate=rate)

from audiolazy import *

rate = 44100s, Hz = sHz(rate)with AudioIO(True) as player: player.play(sinusoid(440 * Hz), rate=rate)

from audiolazy import *

rate = 44100s, Hz = sHz(rate)with AudioIO(True) as player: player.play(sinusoid(440 * Hz), rate=rate)

Multithread!

Page 12: (2013-05-03) AudioLazy - Slides

Síntese

● Aditiva– Senóide (sinusoid)

– Table-lookup● Senóide (sin_table)● Dente de serra

(saw_table)

● AM– Modulação em amplitude

– Modulação em anel

● FM– Exemplo em wxPython

● Subtrativa– Karplus-Strong

● Examplo com os corais de J. S. Bach (Music21)

● Personalizada– Construtor da classe

Stream

– Decorador tostream

Page 13: (2013-05-03) AudioLazy - Slides

Parte 3

StreamStream

Page 14: (2013-05-03) AudioLazy - Slides

Armazenamento: Estratégias para avaliação de expressões

● Antecipada (eager)– Chamada por valor (call by value)

– Avaliação ocorre antes da chamada

● Tardia (lazy)– Chamada por nome (call by name)

● Reavaliação a cada chamada

– Chamada por necessidade (call by need)● Memoize / Cache● Há quem considere essa a única forma de avaliação

“preguiçosa” (lazy)

Page 15: (2013-05-03) AudioLazy - Slides

Avaliação tardia

● Evita cálculos desnecessários● Estruturas de tamanho indefinido

– Potencialmente infinitas (e.g. contador)

● Fluxo de controle como abstração

In [1]: def gerador_123(): ...: val = 1 ...: while True: ...: yield val ...: val = val + 1 if val < 3 else 1 ...:

In [2]: gerador_123Out[2]: <function __main__.gerador_123>

In [3]: gerador_123()Out[3]: <generator object gerador_123 at 0x...>

In [1]: def gerador_123(): ...: val = 1 ...: while True: ...: yield val ...: val = val + 1 if val < 3 else 1 ...:

In [2]: gerador_123Out[2]: <function __main__.gerador_123>

In [3]: gerador_123()Out[3]: <generator object gerador_123 at 0x...>

In [4]: sinal = gerador_123()

In [5]: sinal.next()Out[5]: 1

In [6]: sinal.next()Out[6]: 2

In [7]: sinal.next()Out[7]: 3

In [8]: sinal.next()Out[8]: 1

In [4]: sinal = gerador_123()

In [5]: sinal.next()Out[5]: 1

In [6]: sinal.next()Out[6]: 2

In [7]: sinal.next()Out[7]: 3

In [8]: sinal.next()Out[8]: 1

Desempenho!

Page 16: (2013-05-03) AudioLazy - Slides

Para áudio, o que precisamos?

● Sequência de símbolos– Símbolos são números, normalmente

● Tempo real– Dados (elementos da sequência) inexistentes em

tempo de compilação

– Duração indefinida

– Não é necessário computar tudo para começar a apresentar o resultado

Avaliação tardia!

Page 17: (2013-05-03) AudioLazy - Slides

Classe Stream

● Representa fluxo de informação (áudio)● Iterável heterogêneo com operadores (baseado no

NumPy) e avaliação tardia● Ausência de índices

– Limite de representação inteira (32 bits estouraria em 27:03:12)

In [1]: from audiolazy import Stream

In [2]: dados = Stream(5, 7, 1, 2, 5, 3, 2) # Periódico

In [3]: dados2 = Stream(0, 1) # Idem

In [4]: (dados + dados2).take(15)Out[4]: [5, 8, 1, 3, 5, 4, 2, 6, 7, 2, 2, 6, 3, 3, 5]

In [1]: from audiolazy import Stream

In [2]: dados = Stream(5, 7, 1, 2, 5, 3, 2) # Periódico

In [3]: dados2 = Stream(0, 1) # Idem

In [4]: (dados + dados2).take(15)Out[4]: [5, 8, 1, 3, 5, 4, 2, 6, 7, 2, 2, 6, 3, 3, 5]

Page 18: (2013-05-03) AudioLazy - Slides

Classe Stream

● Métodos, atributos e propriedades são aplicados elemento a elemento– Exceto “take”, “blocks” e outros da própria classe

Stream

● Finito ou de finalização indeterminada

In [5]: Stream([2, 3, 4]).take(5) # Lista de entradaOut[5]: [2, 3, 4]

In [6]: Stream(2, 3, 4).take(5) # Números de entradaOut[6]: [2, 3, 4, 2, 3]

In [7]: Stream(*[2, 3, 4]).take(5) # Lista com "*"Out[7]: [2, 3, 4, 2, 3]

In [8]: (2 * Stream([1 + 2j, -3j, 7]).real).take(inf)Out[8]: [2.0, 0.0, 14]

In [5]: Stream([2, 3, 4]).take(5) # Lista de entradaOut[5]: [2, 3, 4]

In [6]: Stream(2, 3, 4).take(5) # Números de entradaOut[6]: [2, 3, 4, 2, 3]

In [7]: Stream(*[2, 3, 4]).take(5) # Lista com "*"Out[7]: [2, 3, 4, 2, 3]

In [8]: (2 * Stream([1 + 2j, -3j, 7]).real).take(inf)Out[8]: [2.0, 0.0, 14]

Page 19: (2013-05-03) AudioLazy - Slides

Decorador tostream:Geradores convertidos em Stream

● Função = Decorador(Função)

In [1]: from audiolazy import tostream

In [2]: @tostream ...: def impulse(): ...: yield 1 ...: while True: ...: yield 0 ...:

In [3]: impulse # De fato, uma funçãoOut[3]: <function __main__.impulse>

In [4]: impulse() # Devolve um objeto StreamOut[4]: <audiolazy.lazy_stream.Stream at 0x30824d0>

In [5]: impulse().take(5)Out[5]: [1, 0, 0, 0, 0]

In [6]: (impulse() + 1).take(5) # Outro objeto instanciadoOut[6]: [2, 1, 1, 1, 1]

In [1]: from audiolazy import tostream

In [2]: @tostream ...: def impulse(): ...: yield 1 ...: while True: ...: yield 0 ...:

In [3]: impulse # De fato, uma funçãoOut[3]: <function __main__.impulse>

In [4]: impulse() # Devolve um objeto StreamOut[4]: <audiolazy.lazy_stream.Stream at 0x30824d0>

In [5]: impulse().take(5)Out[5]: [1, 0, 0, 0, 0]

In [6]: (impulse() + 1).take(5) # Outro objeto instanciadoOut[6]: [2, 1, 1, 1, 1]

Page 20: (2013-05-03) AudioLazy - Slides

Processamento em bloco

● Stream.blocks(size, hop)– Qualquer salto (hop) positivo

– Se mudar a saída, a mudança persistirá na próxima saída quando hop < size

● Saídas são a mesma fila circular implementada como collections.deque

In [1]: data = Stream([1, 2, 3])

In [2]: blks = data.blocks(size=2, hop=1)

In [3]: [list(blk) for blk in blks]Out[3]: [[1, 2], [2, 3]]

In [1]: data = Stream([1, 2, 3])

In [2]: blks = data.blocks(size=2, hop=1)

In [3]: [list(blk) for blk in blks]Out[3]: [[1, 2], [2, 3]]

Page 21: (2013-05-03) AudioLazy - Slides

SymPy

Page 22: (2013-05-03) AudioLazy - Slides

Parte 4

FiltrosFiltros

Page 23: (2013-05-03) AudioLazy - Slides

Filtros LTI(Lineares e invariantes no tempo)

““Digital signal processing is mainly Digital signal processing is mainly based on linear time-invariant based on linear time-invariant

systems.systems.””

(Dutilleux, Dempwolf, Holters e Zölzer(Dutilleux, Dempwolf, Holters e ZölzerDAFx, segunda edição, capítulo 4, p. 103)DAFx, segunda edição, capítulo 4, p. 103)

Page 24: (2013-05-03) AudioLazy - Slides

Ressonador com valor exato

Page 25: (2013-05-03) AudioLazy - Slides

Gammatone

● Três modelos– Slaney

– Klapuri● 4 ressonadores em cascata

– Implementação genérica (qualquer ordem)● Teoremas

Page 26: (2013-05-03) AudioLazy - Slides

Derivadas

● Prova por indução

Page 27: (2013-05-03) AudioLazy - Slides

Parte 5

MIRMIR((Music Information RetrievalMusic Information Retrieval))

Page 28: (2013-05-03) AudioLazy - Slides

Altura, croma, timbre

● Som de Shepard– Altura (pitch height)

– Croma (pitch chroma)

● Timbre– Brilho

– Envoltória espectral

– Envoltória dinâmica

Page 29: (2013-05-03) AudioLazy - Slides

Série harmônica

● F0, 2F0, 3F0, 4F0 …– 100 Hz, 200 Hz, 300 Hz... Comb!

freqs = [str2freq(note) for note in "E2 G#2 B2".split()] # Mi maiorfilt = ParallelFilter(comb.tau(freq_to_lag(freq * Hz), .1 * s) for freq in freqs)filt.plot(samples=8192, rate=rate, min_freq=220*Hz, max_freq=880*Hz).show()

freqs = [str2freq(note) for note in "E2 G#2 B2".split()] # Mi maiorfilt = ParallelFilter(comb.tau(freq_to_lag(freq * Hz), .1 * s) for freq in freqs)filt.plot(samples=8192, rate=rate, min_freq=220*Hz, max_freq=880*Hz).show()

Inteiros?Racionais?Primos?

2+ oitava

Page 30: (2013-05-03) AudioLazy - Slides

ZCRTaxa de cruzamentos no zero

In [15]: pitch1.take(10) # Resultado em HzOut[15]:[872.0947265625001, 882.861328125, 872.0947265625001, 882.861328125, 882.861328125, 882.861328125, 882.861328125, 872.0947265625001, 882.861328125, 872.0947265625001]

In [16]: freq2str(pitch1).take(10) # Resultado em nomes de notasOut[16]:['A5+5.62%', 'A5+5.62%', 'A5+5.62%', 'A5+5.62%', 'A5+5.62%', 'A5-15.62%', 'A5+5.62%', 'A5-15.62%', 'A5+5.62%', 'A5+5.62%']

In [15]: pitch1.take(10) # Resultado em HzOut[15]:[872.0947265625001, 882.861328125, 872.0947265625001, 882.861328125, 882.861328125, 882.861328125, 882.861328125, 872.0947265625001, 882.861328125, 872.0947265625001]

In [16]: freq2str(pitch1).take(10) # Resultado em nomes de notasOut[16]:['A5+5.62%', 'A5+5.62%', 'A5+5.62%', 'A5+5.62%', 'A5+5.62%', 'A5-15.62%', 'A5+5.62%', 'A5-15.62%', 'A5+5.62%', 'A5+5.62%']

pitch1 = zcross_pitch(data1) / Hzpitch1 = zcross_pitch(data1) / Hz

@tostreamdef zcross_pitch(sig, size=2048): "Devolve a altura em cada bloco com o dado tamanho" for blk in zcross(sig, hysteresis=.2).blocks(size): yield lag_to_freq(2. * size / sum(blk))

@tostreamdef zcross_pitch(sig, size=2048): "Devolve a altura em cada bloco com o dado tamanho" for blk in zcross(sig, hysteresis=.2).blocks(size): yield lag_to_freq(2. * size / sum(blk))

data1 = .5 * sinusoid(880 * Hz)data1 += .5 * saw_table(880*3 * Hz)data1 *= .9 + .1 * white_noise()

data1 = .5 * sinusoid(880 * Hz)data1 += .5 * saw_table(880*3 * Hz)data1 *= .9 + .1 * white_noise()

Page 31: (2013-05-03) AudioLazy - Slides

AMDF (Average Magnitude Difference Function)

Page 32: (2013-05-03) AudioLazy - Slides

Ainda sobre frequência fundamental e periodicidade ...

● SDF (Square Difference Function)

● ACF (Autocorrelação)– Inverso à AMDF e SDF (aqui queremos o maior valor)

● MPM (McLeod e Wyvill)– Utilizar ACF para “normalizar” SDF DFT ?!?

Page 33: (2013-05-03) AudioLazy - Slides

AutocorrelaçãoTranscrição por envoltória dinâmica

Page 34: (2013-05-03) AudioLazy - Slides

Transformada discreta de Hilbert

Page 35: (2013-05-03) AudioLazy - Slides

Algoritmo de Klapuri (2008)Múltiplas frequências fundamentais

Page 36: (2013-05-03) AudioLazy - Slides

Algoritmo de Klapuri (2008)Múltiplas frequências fundamentais

Page 37: (2013-05-03) AudioLazy - Slides

Algoritmo de Klapuri (2008)Múltiplas frequências fundamentais

Page 38: (2013-05-03) AudioLazy - Slides

Algoritmo de Klapuri (2008)Múltiplas frequências fundamentais

white_noise() * (1 + sinusoid(185 * Hz))white_noise() * (1 + sinusoid(185 * Hz))

Page 39: (2013-05-03) AudioLazy - Slides

Algoritmo de Klapuri (2008)Múltiplas frequências fundamentais

● Envoltória espectral● Adaptações

– Evitar repetição

– Polifonia máxima

● Sempre acerta pelo menos uma nota

● Sem otimizações

Entrada “G2 D4 D5”Saída [296.081543, 589.471436, 294.735718]

[ D4 + 14.19% , D5 + 6.3% , D4 + 6.3% ]Entrada “C1 C2 C3 C4”Saída [263.781738, 131.890869, 66.3940430, 44.1430664]

[ C4 + 14.21% , C3 + 14.21% , C2 + 25.95% , F1 + 19.31% ]Entrada “F#4 G4 G#4”Saída [44.1430664, 48.4497070, 371.4477538]

[ F1 + 19.31% , G1 − 19.53% , F#4 + 6.79% ]

Page 40: (2013-05-03) AudioLazy - Slides

from audiolazy import *

rate = 22050s, Hz = sHz(rate)size = 512table = sin_table.harmonize({1: 1, 2: 5, 3: 3, 4: 2, 6: 9, 8: 1}).normalize()

data = table(str2freq("Bb3") * Hz).take(size) # Nota si bemol da 3a oitavafilt = lpc(data, order=14) # Filtro de análiseG = 1e-2 # Ganho apenas para alinhamento na visualização com a DFT

# Filtro de síntese(G / filt).plot(blk=data, rate=rate, samples=1024, unwrap=False).show()

from audiolazy import *

rate = 22050s, Hz = sHz(rate)size = 512table = sin_table.harmonize({1: 1, 2: 5, 3: 3, 4: 2, 6: 9, 8: 1}).normalize()

data = table(str2freq("Bb3") * Hz).take(size) # Nota si bemol da 3a oitavafilt = lpc(data, order=14) # Filtro de análiseG = 1e-2 # Ganho apenas para alinhamento na visualização com a DFT

# Filtro de síntese(G / filt).plot(blk=data, rate=rate, samples=1024, unwrap=False).show()

Envoltória espectralLPC - Predição Linear

Page 41: (2013-05-03) AudioLazy - Slides

Decomposição cromática

● Filtros gammatone– Paralelo

● OitavasIn [1]: from audiolazy import octaves

In [2]: octaves(440)Out[2]: [27.5, 55.0, 110.0, 220.0, 440, 880, 1760, 3520, 7040, 14080]

In [1]: from audiolazy import octaves

In [2]: octaves(440)Out[2]: [27.5, 55.0, 110.0, 220.0, 440, 880, 1760, 3520, 7040, 14080]

from __future__ import divisionfrom audiolazy import *

def cromafb(classes=12, rate): s, Hz = sHz(rate) cg = gammatone_erb_constants(4)[0] fb = 440

return [ ParallelFilter( gammatone.sampled(f*Hz, cg*erb(f)) for f in octaves(fb * 2**(n/classes)) ) for n in xrange(classes) ]

rate = 44100bank = cromafb(rate=rate)bank[0].plot(freq_scale="log", rate=rate)

from __future__ import divisionfrom audiolazy import *

def cromafb(classes=12, rate): s, Hz = sHz(rate) cg = gammatone_erb_constants(4)[0] fb = 440

return [ ParallelFilter( gammatone.sampled(f*Hz, cg*erb(f)) for f in octaves(fb * 2**(n/classes)) ) for n in xrange(classes) ]

rate = 44100bank = cromafb(rate=rate)bank[0].plot(freq_scale="log", rate=rate)

Page 42: (2013-05-03) AudioLazy - Slides

Cromagrama

Page 43: (2013-05-03) AudioLazy - Slides

Diferenciação

Page 44: (2013-05-03) AudioLazy - Slides

Parte 6

Aspectos de ImplementaçãoAspectos de Implementação

Page 45: (2013-05-03) AudioLazy - Slides

AbstractOperatorOverloaderMeta

● Metaclasse– Classe cujas instâncias são classes

● Abstrata– Classe com recursos especificados porém sem

implementação

● Sobrecarga massiva de operadores:– Binários

– Binários reversos

– Unários

Page 46: (2013-05-03) AudioLazy - Slides

Objeto windowUm dicionário de estratégias

In [1]: from audiolazy import window

In [2]: window # Vejamos as estratégias disponíveisOut[2]:{('bartlett',): <function audiolazy.lazy_analysis.bartlett>, ('blackman',): <function audiolazy.lazy_analysis.blackman>, ('hamming',): <function audiolazy.lazy_analysis.hamming>, ('hann', 'hanning'): <function audiolazy.lazy_analysis.hann>, ('rectangular', 'rect'): <function audiolazy.lazy_analysis.rectangular>, ('triangular', 'triangle'): <function audiolazy.lazy_analysis.triangular>}

In [3]: window["rect"](3) # Obtém a estratégia, chamando com 1 argumentoOut[3]: [1.0, 1.0, 1.0]

In [4]: window.triangle(3) # Idem, mas feito com outra sintaxe (dicionário)Out[4]: [0.5, 1.0, 0.5]

In [5]: hm_wnd = window.hamming # Referenciando fora do dicionário

In [6]: hm_wnd # Esta estratégia é uma função comumOut[6]: <function audiolazy.lazy_analysis.hamming>

In [1]: from audiolazy import window

In [2]: window # Vejamos as estratégias disponíveisOut[2]:{('bartlett',): <function audiolazy.lazy_analysis.bartlett>, ('blackman',): <function audiolazy.lazy_analysis.blackman>, ('hamming',): <function audiolazy.lazy_analysis.hamming>, ('hann', 'hanning'): <function audiolazy.lazy_analysis.hann>, ('rectangular', 'rect'): <function audiolazy.lazy_analysis.rectangular>, ('triangular', 'triangle'): <function audiolazy.lazy_analysis.triangular>}

In [3]: window["rect"](3) # Obtém a estratégia, chamando com 1 argumentoOut[3]: [1.0, 1.0, 1.0]

In [4]: window.triangle(3) # Idem, mas feito com outra sintaxe (dicionário)Out[4]: [0.5, 1.0, 0.5]

In [5]: hm_wnd = window.hamming # Referenciando fora do dicionário

In [6]: hm_wnd # Esta estratégia é uma função comumOut[6]: <function audiolazy.lazy_analysis.hamming>

Page 47: (2013-05-03) AudioLazy - Slides

Polinômios

● Necessário para os filtros lineares● Baseados em dicionário

– Memória

– Expoente negativo (Laurent)

– Expoente fracionário (soma de potências)

● Coeficientes podem ser objetos Stream, símbolos do SymPy, etc.

Page 48: (2013-05-03) AudioLazy - Slides

Filtros

● Implementação direta I– Evita multiplicação por 1

– Não cria os termos com coeficiente nulo

– Ainda ineficiente quando longo (e.g. FIR, comb)

● JIT (Just in Time)– Cada filtro é criado e compilado em tempo de

execução

– Permite filtros variantes no tempo gerais e eficientes

Page 49: (2013-05-03) AudioLazy - Slides

Quantização em ponto flutuante

Page 50: (2013-05-03) AudioLazy - Slides

Módulos

Nome Descrição

lazy_analysis Análise de áudio

lazy_auditory Modelagem do aparato auditivo humano periférico

lazy_core Núcleo com as três classes de fundamentação do pacote

lazy_filters Filtros

lazy_io Gravação e reprodução de áudio (via PyAudio), multi-thread

lazy_itertools Conteúdo da itertools “decorado” ou adaptado para objetos Stream

lazy_lpc Codificação linear preditiva (LPC)

lazy_math Funções matemáticas para uso em iteráveis com manutenção de tipo

lazy_midi Representação MIDI e relações entre nota e frequência (altura)

lazy_misc Diversas ferramentas de uso geral e constantes

lazy_poly Polinômios

lazy_stream Definição da classe Stream e derivadas

lazy_synth Pequeno sintetizador

Page 51: (2013-05-03) AudioLazy - Slides

Classes

Nome Bases (herança) Módulo Descrição

AudioIO object lazy_io Reprodutor/gravador de áudio

AudioThread threading.Thread lazy_io Thread representando objetos sendo reproduzidos

LinearFilterProperties object lazy_filters Mixin com conversores de propriedades de filtros lineares

LinearFilter LinearFilterProperties lazy_filters Filtro linear

ZFilter LinearFilter lazy_filters Filtro linear representado por equações em Z

FilterList list, LinearFilterProperties lazy_filters Lista de filtros

CascadeFilter FilterList lazy_filters Filtros em cascata

ParallelFilter FilterList lazy_filters Filtros em paralelo

Poly object lazy_poly Polinômios, polinômios de Laurent, soma de potências

TableLookup object lazy_synth Sintetizador por consulta à tabela

MultiKeyDict dict lazy_core Dicionário multi-chave

StrategyDict MultiKeyDict lazy_core Dicionário de estratégias

StrategyDictInstance StrategyDict lazy_core Uma classe para cada dicionário de estratégias

Stream collections.Iterable lazy_stream Iterável com operadores elemento a elemento e avaliação tardia

ControlStream Stream lazy_stream Stream que devolve um valor controlável, permitindo interatividade

Streamix Stream lazy_stream Misturador (mixer) de objetos Stream baseado na temporização do MIDI

StreamTeeHub Stream lazy_stream Gerenciador de cópias de Stream

MemoryLeakWarning Warning lazy_stream Número de usos de um StreamTeeHub menor que o especificado

ParCorError ZeroDivisionError lazy_lpc Erro ao tentar obter coeficientes PARCOR

Page 52: (2013-05-03) AudioLazy - Slides

Diagrama de classes(Relações internas)

● Classes “centrais”– AbstractOperatorOverloaderMeta

– Stream

● StrategyDict possui instâncias– Diagrama omite informações importantes

Page 53: (2013-05-03) AudioLazy - Slides

Dicionários de estratégias presentes na AudioLazy

Nome Estratégias Módulo Descrição

window 6 lazy_analysis Funções de janelamento ou apodização

envelope 3 lazy_analysis Filtros de obtenção de envoltória dinâmica

maverage 3 lazy_analysis Criador de filtros de média móvel

erb 2 lazy_auditory Largura de banda equivalente retangular (ERB)

gammatone 3 lazy_auditory Filtros gammatone

comb 3 lazy_filters Filtros comb

resonator 4 lazy_filters Ressonadores

lowpass 2 lazy_filters Passa-baixas

highpass 1 lazy_filters Passa-altas

lpc 5 lazy_lpc Codificação linear preditiva (LPC)

Page 54: (2013-05-03) AudioLazy - Slides

Documentação● Docstrings: documentação no código

– Uso em ambientes interativos

– reStructuredText

– Organização em seções● Spyder

● Sphinx– Conversão automática do sistema de seções para o formato do

Sphinx

– Muitos formatos: LaTeX (PDF, DVI, PS), ePUB, HTML, TexInfo, etc.

● Apresentação, instruções de instalação e exemplos básicos– Integração com MatPlotLib, Music21, wxPython, Tkinter, etc.

Page 55: (2013-05-03) AudioLazy - Slides

Exemplos de implementação de partes da AudioLazy

def levinson_durbin(acdata, order):

def inner(a, b): return sum(acdata[abs(i-j)] * ai * bj for i, ai in enumerate(a.numlist) for j, bj in enumerate(b.numlist) )

A = ZFilter(1) for m in range(1, order + 1): B = A(1 / z) * z ** -m A -= inner(A, z ** -m) / inner(B, B) * B return A

def levinson_durbin(acdata, order):

def inner(a, b): return sum(acdata[abs(i-j)] * ai * bj for i, ai in enumerate(a.numlist) for j, bj in enumerate(b.numlist) )

A = ZFilter(1) for m in range(1, order + 1): B = A(1 / z) * z ** -m A -= inner(A, z ** -m) / inner(B, B) * B return A

Page 56: (2013-05-03) AudioLazy - Slides

Parte 7

FinalizaçãoFinalização

Page 57: (2013-05-03) AudioLazy - Slides

Situação atual (PyPI)

VersãoDownloads

(2013-02-15)Downloads

(2013-05-01)

0.01 432 707

0.02 432 709

0.03 209 491

0.04 Não existia 328

Valores mínimos

Page 58: (2013-05-03) AudioLazy - Slides

Conclusões

● Tempo real em Python● Expressividade + avaliação tardia

– Modularidade

● Multiparadigma– Funcional

– Reflexivo

– Objetos

● Filtros digitais a partir de filtros analógicos● Orientação a testes● Padrão IEEE 754

Page 59: (2013-05-03) AudioLazy - Slides

CPython PyPy

AudioLazy 271.49 ms 57.98 ms 53.24 ms

NumPy 19.45 ms 22.78 ms 21.06 ms

Benchmark: Síntese FM

Page 60: (2013-05-03) AudioLazy - Slides

Possíveis continuações para o desenvolvimento da AudioLazy

● Análise– FFT– Heurísticas para transcrição– Outras transformadas

● Modelagem audição– Outros modelos (Lyon, Seneff,

gamma chirp, etc.)– Conversão entre x-dB e ERB

● Síntese e processamento– Wah, phaser, flanger, chorus, eco,

compressor, noise gate, limiter, …

● Testes– 100%– Mock MatPlotLib

● Filtros– Atrasos/expoentes variantes no tempo– Interpolação– Frações parciais– Otimização– Escrita no tempo– SymPy– Treliça

● Outros– Python 3.3– Integrar com PureData– I/O MIDI– Plugins (e.g. Vamp)– Tutorial– Logo

Page 61: (2013-05-03) AudioLazy - Slides

Obrigado!

Perguntas?Perguntas?

Fork me on GitHubFork me on GitHubhttps://github.com/danilobellini/audiolazyhttps://github.com/danilobellini/audiolazy