Successfully reported this slideshow.

(2013-05-20) [DevInSampa] AudioLazy - DSP expressivo e em tempo real para o Python

2

Share

Loading in …3
×
1 of 47
1 of 47

(2013-05-20) [DevInSampa] AudioLazy - DSP expressivo e em tempo real para o Python

2

Share

Download to read offline

Slides da apresentação no DevInSampa 2013 (18 de maio), com algumas complementações e correções. Esses mesmos slides foram utilizados na apresentação feita no fisl 2014 (2013-07-05).

Slides da apresentação no DevInSampa 2013 (18 de maio), com algumas complementações e correções. Esses mesmos slides foram utilizados na apresentação feita no fisl 2014 (2013-07-05).

More Related Content

More from Danilo J. S. Bellini

Related Audiobooks

Free with a 14 day trial from Scribd

See all

(2013-05-20) [DevInSampa] AudioLazy - DSP expressivo e em tempo real para o Python

  1. 1. AudioLazy - DSP expressivo e em tempo real para o Python http://pypi.python.org/pypi/audiolazyhttp://pypi.python.org/pypi/audiolazy Copyright (C) 2012-2013Copyright (C) 2012-2013 Danilo de Jesus da Silva Bellini <danilo.bellini@gmail.com>Danilo de Jesus da Silva Bellini <danilo.bellini@gmail.com>
  2. 2. Parte 1 Síntese! Show me the code! Go go go!Go go go! Não, Python!Não, Python!
  3. 3. “Hello world” em áudio ● Tocar uma senóide – Console interativo – Script from audiolazy import * rate = 44100 s, Hz = sHz(rate) th = AudioIO().play(sinusoid(440 * Hz), rate=rate) from audiolazy import * rate = 44100 s, Hz = sHz(rate) th = AudioIO().play(sinusoid(440 * Hz), rate=rate) from audiolazy import * rate = 44100 s, Hz = sHz(rate) with AudioIO(True) as player: player.play(sinusoid(440 * Hz), rate=rate) from audiolazy import * rate = 44100 s, Hz = sHz(rate) with AudioIO(True) as player: player.play(sinusoid(440 * Hz), rate=rate) Multithread!
  4. 4. Síntese ● ControlStream – Property “value” – Permite interatividade ● Tempo real ● Síntese – Ring Modulation - Senóide * Senóide – AM - Senóide * (1 + Senóide) – FM - Senóide(Senóide) – Subtrativa (e.g. Karplus-Strong) – Aditiva (e.g. TableLookup) Imagem da Wikipedia Exemplos!!!wxPython, Music21
  5. 5. Parte 2 Queremos mais itertools!!! repeat chain count imap ifilter izip ...repeat chain count imap ifilter izip ... Stream !Stream !
  6. 6. Container para áudio ● Tempo real – Amostras (dados/elementos) inexistentes... ● ...em tempo de compilação (dados a serem coletados) ● ...em tempo de execução (dados criados no futuro) – Duração possivelmente indefinida – Não deve ser necessário computar tudo para começar a apresentar o resultado ● Resultados parciais ● Para cada amostra de entrada, deve haver uma de saída – Minimizar lag (atraso) entre entrada e saída Avaliação tardia!
  7. 7. Classe Stream ● Iterável ● Heterogêneo ● Operadores – Elementwise/broadcast como no NumPy ● Avaliação tardia – Lembra geradores ● Já estávamos usando! – sinusoid, karplus_strong, ControlStream, ... ● 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]
  8. 8. Classe Stream ● Métodos, atributos e propriedades são aplicados elemento a elemento – Exceto quando existe na classe Stream (“take”, “blocks”, “peek”, “skip”, “limit”, ...) ● Finito ou de finalização indeterminada In [5]: Stream([2, 3, 4]).take(5) # Lista de entrada Out[5]: [2, 3, 4] In [6]: Stream(2, 3, 4).take(5) # Números de entrada Out[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 entrada Out[5]: [2, 3, 4] In [6]: Stream(2, 3, 4).take(5) # Números de entrada Out[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]
  9. 9. Decorador tostream: Geradores convertidos em Stream ● Já foi aplicado a TODOS os itertools (e.g. count)!!! In [1]: from audiolazy import tostream In [2]: @tostream ...: def impulse(): ...: yield 1 ...: while True: ...: yield 0 ...: In [3]: impulse # De fato, uma função Out[3]: <function __main__.impulse> In [4]: impulse() # Devolve um objeto Stream Out[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 instanciado Out[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ção Out[3]: <function __main__.impulse> In [4]: impulse() # Devolve um objeto Stream Out[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 instanciado Out[6]: [2, 1, 1, 1, 1] Síntese personalizada! (Acesso direto às amostras)
  10. 10. 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, 4, 5]) In [2]: blks = data.blocks(size=2, hop=1) In [3]: [list(blk) for blk in blks] Out[3]: [[1, 2], [2, 3], [3, 4], [4, 5]] In [1]: data = Stream([1, 2, 3, 4, 5]) In [2]: blks = data.blocks(size=2, hop=1) In [3]: [list(blk) for blk in blks] Out[3]: [[1, 2], [2, 3], [3, 4], [4, 5]]
  11. 11. Parte 3 FiltrosFiltros
  12. 12. Filtros LTI (Lineares e invariantes no tempo) ““Digital signal processing is mainlyDigital signal processing is mainly based on linear time-invariantbased on linear time-invariant systems.systems.”” (Dutilleux, Dempwolf, Holters e Zölzer(Dutilleux, Dempwolf, Holters e Zölzer DAFx, segunda edição, capítulo 4, p. 103)DAFx, segunda edição, capítulo 4, p. 103)
  13. 13. Transformada Z ● Definição: ● Interpretação: –Atrasoem k amostras! ● Muitos “infinitos” – Teoremas ● Possibilitam o uso prático (eliminam os “infinitos”)
  14. 14. Exemplo de transformada Z ● Acumulador ● Média móvel Tá legal...mas, na prática, cadê o tal código?! Saída / Entrada, ou H(z) = Y(z) / X(z)
  15. 15. Objeto “z” In [1]: from audiolazy import z, Stream, maverage In [2]: M = 5 In [3]: media_movel_5 = (1 - z ** -M) / (M * (1 - z ** -1)) In [4]: acumulador = 1 / (1 - z ** -1) In [5]: media_movel_5(Stream(5)).take(10) Out[5]: [1.0, 2.0, 3.0, 4.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0] In [6]: acumulador(Stream(5)).take(10) Out[6]: [5.0, 10.0, 15.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0] In [7]: maverage.recursive(4) Out[7]: 0.25 - 0.25 * z^-4 ------------------ 1 - z^-1 In [1]: from audiolazy import z, Stream, maverage In [2]: M = 5 In [3]: media_movel_5 = (1 - z ** -M) / (M * (1 - z ** -1)) In [4]: acumulador = 1 / (1 - z ** -1) In [5]: media_movel_5(Stream(5)).take(10) Out[5]: [1.0, 2.0, 3.0, 4.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0] In [6]: acumulador(Stream(5)).take(10) Out[6]: [5.0, 10.0, 15.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0] In [7]: maverage.recursive(4) Out[7]: 0.25 - 0.25 * z^-4 ------------------ 1 - z^-1 Filtros LTI, em geral:
  16. 16. Filtros prontos! ● Média móvel ● Ressonadores ● Comb ● Passa-baixas e passa-altas ● Gammatone (Patterson-Holdsworth, audição) – Slaney – Klapuri ● 4 ressonadores em cascata – Implementação genérica (qualquer ordem) ● Teoremas (parte de meu mestrado) Filtros variantes no tempo! Coeficientes “a” em de a * z ** -k podem ser objetos Stream)
  17. 17. Plot (AudioLazy + MatPlotLib)! ● DTFT - Caso particular da transformada Z – O valor de z está na circunferência complexa unitária ● Método plot dos filtros – Resposta em frequência ● Método zplot – Estabilidade do filtro – Pólos: “X” ● Raízes do denominador – Zeros: “O” ● Raízes do numerador X X MatPlotLib faz melhor que isto...
  18. 18. Considerações finais sobre filtros ● Implementação direta I – Evita multiplicação por 1 – Não cria os termos com coeficiente nulo ● JIT (Just in Time) – Cada filtro é criado e compilado em tempo de execução – Permite filtros variantes no tempo gerais e (até certo ponto) eficientes
  19. 19. Parte 4 - Cálculo numérico (e um pouco de simbólico também) Heeeeeey, não era sobre áudio?Heeeeeey, não era sobre áudio?
  20. 20. Exemplos ● Pi (exemplo no repositório da AudioLazy) – Série de Madhava-Gregory-Leibniz ● Fibonacci atan(v)=v− v3 3 + v5 5 − v7 7 + v9 9 ... atan(1)= π 4 In [2]: (z ** -1 / (1 - z ** -1 - z ** -2))(impulse()).take(10) Out[2]: [0.0, 1.0, 1.0, 2.0, 3.0, 5.0, 8.0, 13.0, 21.0, 34.0] In [2]: (z ** -1 / (1 - z ** -1 - z ** -2))(impulse()).take(10) Out[2]: [0.0, 1.0, 1.0, 2.0, 3.0, 5.0, 8.0, 13.0, 21.0, 34.0] h[n]=h[n−1]+h[ n−2]+δ[ n−1]
  21. 21. 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. ● Objeto “x” ● Interpolação (polinômios de Lagrange) In [9]: lagrange.poly([(1, 3), (3, 14), (45, 0)]) Out[9]: -2.89773 + 6.0303 * x - 0.132576 * x^2 In [9]: lagrange.poly([(1, 3), (3, 14), (45, 0)]) Out[9]: -2.89773 + 6.0303 * x - 0.132576 * x^2 In [8]: (x + x ** 2 + x ** -.5)(4) Out[8]: 20.5 In [8]: (x + x ** 2 + x ** -.5)(4) Out[8]: 20.5
  22. 22. SymPy Stream de símbolos
  23. 23. 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 em um dicionário de estratégias: – almost_eq.diff – almost_eq ou almost_eq.bits ∣a−b∣≤l ∣a−b∣≤2(t − s−1)∣a+b∣
  24. 24. Quantização em ponto flutuante Com CascadeFilter Warning: Há textos que nem sempre são claros quanto aos aspectos necessários à implementação para garantir a estabilidade numérica freq = 100 * Hz bw = erb(freq, Hz) * gammatone_erb_constants(4)[0] filt = gammatone.slaney(freq, bw) filt.plot(rate=rate, freq_scale="log", samples=8192).show() freq = 100 * Hz bw = erb(freq, Hz) * gammatone_erb_constants(4)[0] filt = gammatone.slaney(freq, bw) filt.plot(rate=rate, freq_scale="log", samples=8192).show() Sem CascadeFilter
  25. 25. Parte 5 MIRMIR ((Music Information RetrievalMusic Information Retrieval))
  26. 26. Pitch – Shepard ● Som de Shepard – Subir “sem parar” – Exemplo no GitHub ● Duas dimensões: – Altura (pitch height) ● Dimensão “linear” – Croma (pitch chroma) ● Dimensão “circular” ● Lembra Escher →
  27. 27. 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 maior filt = 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 maior filt = 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
  28. 28. ZCR Taxa de cruzamentos no zero In [15]: pitch1.take(10) # Resultado em Hz Out[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 notas Out[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 Hz Out[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 notas Out[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 @tostream def 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)) @tostream def 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() Por quenão DFT?
  29. 29. AMDF (Average Magnitude Difference Function)
  30. 30. Autocorrelação Transcrição por envoltória dinâmica
  31. 31. Decomposição cromática ● Filtros gammatone – Paralelo ● Oitavas 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] 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 division from 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 = 44100 bank = cromafb(rate=rate) bank[0].plot(freq_scale="log", rate=rate) from __future__ import division from 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 = 44100 bank = cromafb(rate=rate) bank[0].plot(freq_scale="log", rate=rate)
  32. 32. Cromagrama
  33. 33. from audiolazy import * rate = 22050 s, Hz = sHz(rate) size = 512 table = 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 oitava filt = lpc(data, order=14) # Filtro de análise G = 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 = 22050 s, Hz = sHz(rate) size = 512 table = 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 oitava filt = lpc(data, order=14) # Filtro de análise G = 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 espectral LPC - Predição Linear Formantes Pode ser utilizado para classificação de vogais
  34. 34. Parte 6 NúcleoNúcleo (Não, não vai explodir...)(Não, não vai explodir...)
  35. 35. 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 ● Utiliza-se da classe OpMethod – Organiza todos os 33 possíveis dunders de operadores Há, entretanto, 35 no Python 2. AbstractOperatorOverloaderMeta insere __div__ e __rdiv__ automaticamente a partir de __truediv__ e __rtruediv__ (quando aplicável)
  36. 36. Objeto window Um dicionário de estratégias In [1]: from audiolazy import window In [2]: window # Vejamos as estratégias disponíveis Out[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 argumento Out[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 comum Out[6]: <function audiolazy.lazy_analysis.hamming> In [1]: from audiolazy import window In [2]: window # Vejamos as estratégias disponíveis Out[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 argumento Out[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 comum Out[6]: <function audiolazy.lazy_analysis.hamming> Também é um iterável por suas estratégias (ver windows_plot.py)
  37. 37. Documentação ● Docstrings: documentação no código – Uso em ambientes interativos – reStructuredText – Organização em seções ● Visualização no Spyder (Rich Text), IPython ● 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. http://pythonhosted.org/audiolazy https://github.com/danilobellini/audiolazy/tree/master/audiolazy/examples
  38. 38. Parte 7 FinalizaçãoFinalização
  39. 39. 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 Suporta Python 2 e 3 com o mesmo código!
  40. 40. Motivações e justificativas No papel... ● Demanda e insatisfação com código existente – Por mim ● Vetores (NumPy, Octave/MatLab)...tempo real? ● Índices...=( – Por outros ● 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)!!! Bad guy...código? ● Base para trabalhos futuros
  41. 41. Resumidamente: The Litmus Test If you have to refer to theIf you have to refer to the documentation every timedocumentation every time you use a module, find (oryou use a module, find (or build) a new module.build) a new module. Até tentei evitar...mas...
  42. 42. 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% 72 lazy_analysis 125 3 60 2 97% 211, 259-260 lazy_auditory 60 0 14 0 100% lazy_compat 42 5 6 1 88% 43, 67-68, 78-79 lazy_core 174 7 78 9 94% 124, 136-138, 346, 471, 478 lazy_filters 509 179 245 117 61% 55, 62, 83, 93, [...] lazy_io 156 43 58 28 67% 89, 141-157, 161, [...] lazy_itertools 37 12 20 13 56% 38, 59-60, 69-74, 100-103 lazy_lpc 128 15 42 7 87% 121, 135-136, [...] lazy_math 61 1 28 0 99% 133 lazy_midi 54 5 26 3 90% 70, 111, 150, 156, 158 lazy_misc 110 9 62 10 89% 156-157, 194, [...] lazy_poly 184 2 124 3 98% 387-388 lazy_stream 175 2 76 4 98% 59, 738 lazy_synth 243 32 118 39 80% 277-299, 428, 430, [...] lazy_text 104 16 70 18 80% 133-148, 207, [...] ------------------------------------------------------------ TOTAL 2206 332 1045 258 82% ========================= 5938 passed in 29.00 seconds ========================= --------------- coverage: platform linux2, python 2.7.3-final-0 ---------------- Name Stmts Miss Branch BrPart Cover Missing ------------------------------------------------------------ __init__ 44 1 18 4 92% 72 lazy_analysis 125 3 60 2 97% 211, 259-260 lazy_auditory 60 0 14 0 100% lazy_compat 42 5 6 1 88% 43, 67-68, 78-79 lazy_core 174 7 78 9 94% 124, 136-138, 346, 471, 478 lazy_filters 509 179 245 117 61% 55, 62, 83, 93, [...] lazy_io 156 43 58 28 67% 89, 141-157, 161, [...] lazy_itertools 37 12 20 13 56% 38, 59-60, 69-74, 100-103 lazy_lpc 128 15 42 7 87% 121, 135-136, [...] lazy_math 61 1 28 0 99% 133 lazy_midi 54 5 26 3 90% 70, 111, 150, 156, 158 lazy_misc 110 9 62 10 89% 156-157, 194, [...] lazy_poly 184 2 124 3 98% 387-388 lazy_stream 175 2 76 4 98% 59, 738 lazy_synth 243 32 118 39 80% 277-299, 428, 430, [...] lazy_text 104 16 70 18 80% 133-148, 207, [...] ------------------------------------------------------------ TOTAL 2206 332 1045 258 82% ========================= 5938 passed in 29.00 seconds ========================= Mock
  43. 43. Testes com oráculos ● 80 c/ o scipy.signal.lfilter ● 64 c/ o subpacote de otimização do SciPy ● 2 c/ o NumPy import pytest p = pytest.mark.parametrize from scipy.signal import lfilter from 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 pytest p = pytest.mark.parametrize from scipy.signal import lfilter from 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
  44. 44. 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
  45. 45. Últimas novidades! ● Python 3! ● Contas com polinômios variantes no tempo ● Métodos das classes Stream e StreamTeeHub – Peek – Skip – Limit ● Polinômios de Lagrange (interpolação) ● Reamostragem (taxa de amostragem variante no tempo) – Isso é útil? (variante no tempo)
  46. 46. 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 por splines – Frações parciais – Otimização – Escrita no tempo – SymPy – Treliça ● Outros – Integrar com PureData – I/O MIDI – Plugins (e.g. Vamp) – Tutorial – Logo
  47. 47. Obrigado! Perguntas?Perguntas? Fork me on GitHubFork me on GitHub https://github.com/danilobellini/audiolazyhttps://github.com/danilobellini/audiolazy

×