andre_teprom escreveu:Na miha opiniao, pra ficar realistico teria de ter vários timers disponiveis no uC. E pra cada um, a saída deveria ser multiplexada por hadware, mas aí o filtro de saída e o ganho iriam variar conforme o tom. Já usei um uC da Texas (MSP430F149) que possuía 7 timers, mas creio que nem isso seja suficiente para essa aplicaçação, já que apesar de nao usarmos os 10 dedos simultaneamente ao tocar o piano, há o efeito da sustentação da tecla atravez do pedal, o que significa talvez na necessidade de mais de uma duzia de tons ao mesmo tempo. Nesse contexto, talvez o problema recaia num microcontrolador SoC, onde voce possa criar alguns recursos de hardware extra. Uma outra abordagem, mas agora mais profissional seria com a utilização de um DSP, mas aí o buraco seria mais embaixo, pois tudo teria de ser feito matematicamente em algoritmo. Enfim, se quizer algo bom, vai ter de suar mais um pouco.
na realidade um unico timer para manter o data rate constante jah eh suficiente!

veja este algoritmo gerador de tom ilustrado no apendice A deste paper da texas:
http://www.ti.com/lit/an/spra096a/spra096a.pdfno caso do paper da texas, eles usam pares de geradores senoidais para gerar pares de tons que sao somados para gerar sinalizacao DTMF. e bom, eh facil imaginar que em telefonia usamos dezenas e dezenas de geradores DTMF em DSPs baratos para gerar e decodificar todo tipo de tom de sinalizacao. o interessante eh que o algoritmo consome muito poucos recursos, o que se traduz em um mar de tons gerados simultaneamente! outra parte interessante eh que sao todos gerados em um unico data rate, o que permite mixar diretamente eles para gerar resultados complexos de forma muito trivial!
basicamente, o gerador de tons trabalha com o seguinte algoritmo para cada amostra:
y[0] = a1*y[1] - y[2] + b0
y[2] = y[1]
y[1] = y[0]
onde vc pre-calcula no excel:
a1 = -2 cos(w)
b0 = A sin(w)
A = amplitude
w =2*PI*f/f0
f = frequencia da senoide
f0 = data rate
y[1] inicial = 0
y[2] inicial = -b0
assim, se vc tem N teclas com N frequencias diferentes, vc vai ter esses N conjuntos de coeficientes pre-calculados. a medida que as teclas sao ativadas, vc vai rodar o algoritmo gerador e ele vai gerar as amostras das respectivas senoides. todas as senoides sao geradas e somadas segundo uma soma ponderada, de modo que provavelmente vc vai ter:
- 1 multiplicacao + 2 somas p/ gerar a amostra de uma frequencia
- 2 copias de dados p/ cada frequencia
- 1 multiplicacao + soma por frequencia para fazer a soma ponderada
o resultado vai ser uma amostra que contem as amostras de todas as senoides somadas e isso vai para o seu DA ou PWM. se vc quiser, pode somar resultados de wave tables, desde que operado no mesmo data rate (existem tecnicas para adaptar o data rate de wave tables).
em termos de performance: sabendo que o cara tem 10 dedos, a composicao de uma amostra com os 10 tons requer 20 multiplicacoes, 20 copias de dados e 30 somas, totalizando 70 operacoes. com loops e controles, talvez chegue a 100 ou 150 instrucoes por amostra. o numero de amostras depende da qualidade desejada, mas supondo 16 bits x 44kHz padrao do CD, sao 44k x 150 instrucoes = 6.6MIPS. qualquer DSP de 20 anos atras ou microcontrolador fajuto de 16 bits que tenha multiplicacao em hardware dah conta de boa. evidentemente, somar 10 amostras de 16 bits vai dar um overflow em um microcontrolador de 16 bits, a menos que vc faca shift previamente. isso pode causar alguma degradacao de qualidade quando comparado a um DSP com acumulador grande, mas funciona. e claro, eh possivel fazer isso em um microcontrolador de 8 bits, as custas de performance para simular inteiros maiores e mais lentos ou as custas de qualidade para trabalhar com inteiros menores e mais rapidos. mas hoje em dia eu diria que qualquer ARM baratinho com registros de 32 bits faz isso com um custo muito baixo e com os pehs nas costas!

um bonus extra de implementar no ARM: vc pode testar todo o conceito matematico previamente no PC, rodando no linux, onde jah tem um acesso facil ao /dev/dsp rodando em 16 bits x 44kHz e compilador gcc. funcionando tudo certo, basta recompilar para o ARM, adaptar algumas firulas de IO e pronto.