Sons polifônicos de um microcontrolador?

14

Posso emitir sons monofônicos alternando um único pino ( a uma taxa variável ) conectado a uma campainha piezo.

Como posso gerar dois sinais de áudio mistos no software para criar polifonia?

Aqui está o código que estou usando para tocar uma música simples.

#define F_CPU 8000000UL // 8MHz
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/delay.h>

// number of timer0 overflows/sec
#define INT_PER_SEC 31250

// Frequencies (in Hz) of notes
#define F_FSH_4 370
#define F_A_4 440
#define F_B_4 494
#define F_E_4 330
#define F_CSH_5 554
#define F_D_5 587
#define F_FSH_5 740
#define F_CSH_4 277
#define F_GSH_4 415

// number of timer0 overflows for notes
#define REST -1 // special case
#define FSH_4 INT_PER_SEC/F_FSH_4
#define A_4 INT_PER_SEC/F_A_4
#define B_4 INT_PER_SEC/F_B_4
#define E_4 INT_PER_SEC/F_E_4
#define CSH_5 INT_PER_SEC/F_CSH_5
#define D_5 INT_PER_SEC/F_D_5
#define FSH_5 INT_PER_SEC/F_FSH_5
#define CSH_4 INT_PER_SEC/F_CSH_4
#define GSH_4 INT_PER_SEC/F_GSH_4

#define SEMIQUAVER_TIME 60  // ms
#define BREATH_TIME 20      // ms

volatile uint32_t intrs = 0;
volatile int32_t curNote = REST;

// TIMER0 overflow
ISR(TIMER0_OVF_vect)
{
    if (curNote == REST)
        intrs = 0;
    else
    {
        intrs++;
        if (intrs >= curNote)
        {
            PORTD ^= _BV(PD4);
            intrs = 0;
        }
    }
}


void play(int32_t note, uint32_t len)
{
    int i;
    curNote = note;
    for (i = 0; i< len; i++)
        _delay_ms(SEMIQUAVER_TIME);
    curNote = REST;
    _delay_ms(BREATH_TIME);
}

int main(void)
{
    /* setup clock divider. Timer0 overflows on counting to 256.
     * 8Mhz / 1 (CS0=1) = 8000000 increments/sec. Overflows every 256, so 31250
     * overflow interrupts/sec */
    TCCR0B |= _BV(CS00);

    // enable overflow interrupts
    TIMSK0 |= _BV(TOIE0);

    // PD4 as output
    DDRD = _BV(PD4);

    TCNT0 = 0;
    intrs = 0;

    curNote = REST;

    // enable interrupts
    sei();

    while (1)
    {
        // Axel F
        play(FSH_4, 2);
        play(REST, 2);
        play(A_4, 3);
        play(FSH_4, 2);
        play(FSH_4, 1);
        play(B_4, 2);
        play(FSH_4, 2);
        play(E_4, 2);
        play(FSH_4, 2);
        play(REST, 2);
        play(CSH_5, 3);
        play(FSH_4, 2);
        play(FSH_4, 1);
        play(D_5, 2);
        play(CSH_5, 2);
        play(A_4, 2);
        play(FSH_4, 2);
        play(CSH_5, 2);
        play(FSH_5, 2);
        play(FSH_4, 1);
        play(E_4, 2);
        play(E_4, 1);
        play(CSH_4, 2);
        play(GSH_4, 2);
        play(FSH_4, 6);
        play(REST, 12);
    }
}
Toby Jaffey
fonte
Ei, isso pode emitir linguagem humana? Quero dizer como palavras?
21910 Ricky_Martins
1
Dê uma olhada em Cantarino - code.google.com/p/tinkerit/wiki/Cantarino
Toby Jaffey
@Joby, o recurso que você deu foi ótimo, mas eu vi a demonstração, na verdade não está dizendo nada audível.
Rick_2047
Não sem um DAC, não.
Toby Jaffey
@Joby, o que você tem com um DAC?
Rick_2047

Respostas:

8

Bem, um truque fácil é usar dois pinos com PWM e amarrá-los aos lados opostos do alto-falante. Em seguida, module cada pino em uma velocidade diferente e você poderá tocar duas notas ao mesmo tempo ... basicamente o alto-falante as está misturando para você. Mais de duas notas e você terá que fazer isso no software, conforme mencionado.

davr
fonte
1
Se você estiver usando PWM (alternando com uma frequência muito maior que o sinal desejado), já poderá misturar vários sinais usando apenas um pino de saída.
Endolith
5

A maneira padrão de obter polifonia é interromper a uma taxa de interrupção fixa (geralmente 8000 Hz ou 44100 Hz), obter um "alto" (+1) ou "baixo" (-1) (ou algo intermediário) de cada fonte de som , some todos os números para obter um total e envie esse número total para o DAC.

Como outros já disseram aqui, com um pouco de inteligência, um PWM de alta velocidade pode substituir um DAC.

A página "polifonia do microcontrolador" fornece mais detalhes e dicas.

davidcary
fonte
3

Eu acho que essa bela e pequena joia do jogo PC DOS usou som polifônico real através do alto-falante do PC: Digger .

Não sei como eles fizeram isso, mas você pode fazer o download do código-fonte C no site.


fonte
Ainda consigo ouvir a música na minha cabeça
Toby Jaffey
2

Isso pode ajudar -> DW PWM simples

Jim
fonte
Para que o Arduino toque as notas de forma assíncrona, você precisará usar um sistema semelhante ao MIDI - com comandos separados para anotações e anotações etc., a biblioteca de tons integrada faz isso, mas não faz polifonia -, mas a versão mais recente parece funciona - code.google.com/p/rogue-code/wiki/ToneLibraryDocumentation
Jim
2

Se você estiver usando o software para cronometrar os eventos do alto-falante, a abordagem mais fácil é provavelmente gerar dois fluxos de dados independentes e alternar entre eles. Essa abordagem pode funcionar muito bem se a saída do alto-falante é controlada por um pino de E / S ou um DAC. Por exemplo:

int seletor;
fase uint16_t [8], frequência [8];

interrupção nula (nula) { seletor ++; seletor & = 7; fase [seletor] + freq [seletor]; DAC_OUT = onda senoidal [fase [seletor] >> 8]; }

A descrição acima é a abordagem essencial que usei em uma caixa de música baseada em PIC em 1996 (usando código de montagem em vez de C). Observe que a taxa de interrupção deve ser 8 vezes a taxa de amostragem efetiva, mas cada interrupção precisa fazer o processamento apenas para uma única voz. Observe que, se a filtragem de saída for boa, essa abordagem produzirá 3 bits a mais de resolução DAC efetiva do que a adição de amostras numericamente e a saída delas, mas gerará muito ruído na taxa de amostragem e múltiplos dela. A filtragem é, portanto, ainda mais importante do que seria.

supercat
fonte
1

Eles costumavam fazer isso em sistemas de jogos antigos e nos dias de " alto-falantes do PC ", mas eu não sei como.

Primeiro palpite: pense na onda que você idealmente faria, imagine-a distorcendo-a em uma forma quadrada fortemente cortada e, em seguida, crie essa forma quadrada alternando sua saída nos momentos apropriados. Teria muita intermodulação , no entanto.

Segundo pensamento: você pode aumentar significativamente a frequência dos sinais analógicos de oscilação e saída no estilo PWM ?

endólito
fonte
2
Lembro-me de olhar para um emulador NES há muito tempo e acredito que eles usaram três formas de onda cada uma com uma frequência programável. Duas ondas quadradas e uma onda triangular.
Mjh2007
... e uma fonte de ruído, aparentemente. en.wikipedia.org/wiki/NES_Sound_Format
endolith 07/04
1

Como já foi mencionado, você pode fazer isso da mesma maneira que costumava ser feito com um alto-falante de PC (que suporta apenas on / off opcionalmente conectado a um controlador PWM.) Basicamente, meu entendimento do método é que você liga e desliga o alto-falante rápido o suficiente para nunca estar totalmente ligado ou desligado (um pouco como o funcionamento de uma fonte de alimentação comutada). Isso deixa o alto-falante constantemente se movendo entre ligado e desligado, gerando um sinal analógico.

As únicas dicas são que você precisa de um alto-falante real (acho que um piezo se move tão rápido que chega cheio e muito rápido) e você precisa ser capaz de alternar o pouco rápido o suficiente. Fiz alguns experimentos e obtive uma velocidade máxima de cerca de 5 MHz, que deveria ser suficiente para um sinal de áudio de 11.025 Hz (provavelmente a melhor qualidade que você poderia obter).

É claro que 11025Hz a 8 bits é de 11 kilobytes / segundo, o que é muito mais rápido que a velocidade de uma porta serial. Isso permitiria que apenas um ou dois segundos de áudio fossem armazenados no flash, então você está praticamente limitado a reproduzir áudio gerado em tempo real, desde que haja tempo de CPU suficiente para mexer no alto-falante!

Existem alguns outros métodos para conseguir isso também, e parece que existe uma implementação para o Arduino do método descrito acima.

Malvineous
fonte
2
Você pode usar um filtro antes do alto-falante para suavizar o PWM, independentemente da velocidade com que o próprio alto-falante se move.
endolith 27/03
1

Reproduza o som A por um momento, como talvez 50 ms, depois toque o B e alterne. A idéia é mudar mais rápido do que o ouvido pode dizer e soará como os dois tocando ao mesmo tempo.

Matt Williamson
fonte
1

Acredito que exista uma biblioteca de tons para o Arduino que faça dois tons. Você poderá adaptar o código ao chip AVR que está usando. Também existem alguns excelentes threads de geração de formas de onda no arduino.cc

Se você decidir adicionar um DAC, tenho um exemplo de oscilador controlado numericamente em http://wiblocks.luciani.org/docs/app-notes/nb1a-nco.html Quatro canais de saída independentes. O quad DAC e a referência custam apenas cerca de US $ 2.

jluciani
fonte
0

Aqui está o meu código para reproduzir 2 músicas ao mesmo tempo. Desculpe, você precisa se registrar no AVR Freaks para obter acesso.

avra
fonte
4
Eu te daria uma upvote se você postou o código aqui, ou para algum lugar Eu não preciso de uma conta ...
Toby Jaffey