当前位置: 动力学知识库 > 问答 > 编程问答 >

c - How to send sound to a speaker

问题描述:

If I were to program a microcontroller (ATMega128) to play a realtone with a speaker, how would I do this?

Do I need to use a digital/analog converter to send different amplitude values, or is it enough with frequency changes? In any case, how would I encode the frequency and amplitude values that the speaker needs to receive? Do I need some kind of frequency multiplexing? I'm not talking about just making simple sounds with the speaker, like one note and then the other. I want to play a real song, with all the instruments, vocals, etc.

网友答案:

Assuming you have an uncompressed 8-bit 22.1 kHz mono wave file:

1) Strip out the header
2) Every 1/22,100th second:
2.1) Read 8 bits
2.2) Use a DAC to convert it to the speaker's voltage range
2.3) Send it to the speaker

This will give you [22.1 kHz/8 bits/mono] quality sound and is a simple way to play realistic samples.

All that frequency stuff is necessary for different synthesizers. The PC speaker, for example is effectively one bit. In order to have different amplitudes (than 'none' and 'maximum') some tricks such as pulse-width modulation might be necessary (shift the frequencies around, like you said, so the speaker's diaphragm effectively has more positions than two).

But you don't need to bother with that. All you need to do is spew out some 22,100 or 44,200 sound samples per second at the speaker, with say 8 or 16 bits per sample to denote the amplitude.

网友答案:

I've tried something similar. First, you will NOT have enough memory on the microcontroller to store a real song. You'll need external memory to handle that. That means using an SPI interface to external flash or EEPROM or something. SD is also good - I believe it's an SPI style interface. There is code out there for the ATMegas to interface to SD cards.

The second big deal is to get the data in a proper format. The way I would do this is to use pulse-width modulation (PWM) to create the varying voltage levels. I believe you have a 16-bit PWM on that microcontroller so you can have 16-bit fidelity on your sound. If you have problems with space you can use 8-bit PWM instead. So your sound data has to be in 8 or 16 bit PCM with 0x0000 being lowest value and 0xFFFF being the highest. If you want high-fidelity music you'll have to have a sampling rate of 44 KHz to get all of the good harmonics and such. I believe this is PCM - same as it is called on the PC.

So then you'll have all of these values - for five minutes of music you'll have 5 * 60 * 44000 = 13,200,000 16 bit values which is 211,200,000 bits (211 megabits, 26.4 megabytes). Those are your storage requirements for raw data. MP3 is a possibility - there are external chips for that, but you'll still have large space requirements.

So every 1/44000th of a second you'll update a value in a PWM register. Your PWM frequency will have to be higher by 4 or 5 - ie, 5 PWM cycles per value.

That's your general algorithm - update values in a PWM register and let it go until the end. You will need at least a filter on the output - limit frequencies to the audible range of 20KHz (more if you're an audiophile). An RC filter will work but I would go for an active filter BECAUSE if you're using PWM your output range will be from 0 to 5V usually with the average voltage being about 2.5V. Speakers don't like DC levels - just signals. Pretty sine waves that have a 0 mean voltage. So your active filter will have to adjust the voltage levels and use dual supplies to provide negative voltages. You can also amplify the signal for the big pumping bass. Just don't blow out your speakers.

MP3 is probably a better alternative to PCM. There are chips out there: http://www.sparkfun.com/commerce/product_info.php?products_id=8892

That is, however, a whole microcontroller. And you already have one. But let's face it - the ATMega isn't going to do MP3 on its own soon no matter how you jazz it up.

It looks like the waveshield referenced above basically does this - uses an SD card for the storage of PCM and an external amplifier for the sound. Good luck!

网友答案:

One method for generating a steady tone is Direct Digital Synthesis. You'll need a DAC, either a dedicated chip or a resistor ladder.

You set up a counter to overflow at the frequency you want to generate, and on each tick of the counter you use it to index a wavetable and get an output value for your DAC.

I've written up a couple of different tone generation techniques for the Arduino here at New Noises From MidiVox. The DAC updating code is specific to MidiVox (and the Adafruit WaveShield's) MCP4921, but the sine wave generation should be generally applicable. I've tried to keep the code mostly general to ATmegas, but there are a couple of Arduino-isms that crept in.

Pasted from that post, here is some code to play a 440Hz tone on an Arduino with an MCP4921 on the SPI bus:

uint16_t sample = 0;

/* incr = freq * (2^16 / 15625) 
 * So for 440Hz, incr = 1845 */
uint16_t incr = 1845;

/* oscillator position */
uint16_t pos = 0;

const uint8_t sine[] = {
    0x80, 0x83, 0x86, 0x89, 0x8C, 0x8F, 0x92, 0x95, 0x98, 0x9B, 0x9E, 0xA2,
    0xA5, 0xA7, 0xAA, 0xAD, 0xB0, 0xB3, 0xB6, 0xB9, 0xBC, 0xBE, 0xC1, 0xC4,
    0xC6, 0xC9, 0xCB, 0xCE, 0xD0, 0xD3, 0xD5, 0xD7, 0xDA, 0xDC, 0xDE, 0xE0,
    0xE2, 0xE4, 0xE6, 0xE8, 0xEA, 0xEB, 0xED, 0xEE, 0xF0, 0xF1, 0xF3, 0xF4,
    0xF5, 0xF6, 0xF8, 0xF9, 0xFA, 0xFA, 0xFB, 0xFC, 0xFD, 0xFD, 0xFE, 0xFE,
    0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFD,
    0xFD, 0xFC, 0xFB, 0xFA, 0xFA, 0xF9, 0xF8, 0xF6, 0xF5, 0xF4, 0xF3, 0xF1,
    0xF0, 0xEE, 0xED, 0xEB, 0xEA, 0xE8, 0xE6, 0xE4, 0xE2, 0xE0, 0xDE, 0xDC,
    0xDA, 0xD7, 0xD5, 0xD3, 0xD0, 0xCE, 0xCB, 0xC9, 0xC6, 0xC4, 0xC1, 0xBE,
    0xBC, 0xB9, 0xB6, 0xB3, 0xB0, 0xAD, 0xAA, 0xA7, 0xA5, 0xA2, 0x9E, 0x9B,
    0x98, 0x95, 0x92, 0x8F, 0x8C, 0x89, 0x86, 0x83, 0x80, 0x7D, 0x7A, 0x77,
    0x74, 0x71, 0x6E, 0x6B, 0x68, 0x65, 0x62, 0x5E, 0x5B, 0x59, 0x56, 0x53,
    0x50, 0x4D, 0x4A, 0x47, 0x44, 0x42, 0x3F, 0x3C, 0x3A, 0x37, 0x35, 0x32,
    0x30, 0x2D, 0x2B, 0x29, 0x26, 0x24, 0x22, 0x20, 0x1E, 0x1C, 0x1A, 0x18,
    0x16, 0x15, 0x13, 0x12, 0x10, 0x0F, 0x0D, 0x0C, 0x0B, 0x0A, 0x08, 0x07,
    0x06, 0x06, 0x05, 0x04, 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06,
    0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0C, 0x0D, 0x0F, 0x10, 0x12, 0x13, 0x15,
    0x16, 0x18, 0x1A, 0x1C, 0x1E, 0x20, 0x22, 0x24, 0x26, 0x29, 0x2B, 0x2D,
    0x30, 0x32, 0x35, 0x37, 0x3A, 0x3C, 0x3F, 0x42, 0x44, 0x47, 0x4A, 0x4D,
    0x50, 0x53, 0x56, 0x59, 0x5B, 0x5E, 0x62, 0x65, 0x68, 0x6B, 0x6E, 0x71,
    0x74, 0x77, 0x7A, 0x7D
};

void setup() {
    cli();

    /* Enable interrupt on timer2 == 127, with clk/8 prescaler. At 16MHz,
       this gives a timer interrupt at 15625Hz. */
    TIMSK2 = (1 << OCIE2A);
    OCR2A = 127;

    /* clear/reset timer on match */
    TCCR2A = 1<<WGM21 | 0<<WGM20; /* CTC mode, reset on match */
    TCCR2B = 0<<CS22 | 1<<CS21 | 0<<CS20; /* clk, /8 prescaler */

    SPCR = 0x50;
    SPSR = 0x01;
    DDRB |= 0x2E;
    PORTB |= (1<<1);

    sei();
}

ISR(TIMER2_COMPA_vect) {
    /* OCR2A has been cleared, per TCCR2A above */
    OCR2A = 127;

    pos += incr;

    /* shift left a couple of bits for more volume */
    sample = sine[highByte(pos)] << 2;

    PORTB &= ~(1<<1);

    /* buffered, 1x gain, active mode */
    SPDR = highByte(sample) | 0x70;
    while (!(SPSR & (1<<SPIF)));

    SPDR = lowByte(sample);
    while (!(SPSR & (1<<SPIF)));

    PORTB |= (1<<1);
}

void loop() {
}

The nifty thing about Direct Digital Synthesis is that it's extremely easy to play multiple tones together (by addition) or mix them at desired volumes (by multiplying by the volume before adding).

I've found that an Arduino can play about 30 tones simultaneously using this method. My particular application is for Hammond organ simulation, and that may prove useful reading as well.

网友答案:

Today it is both easy and cheap to play MP3 files from an 8-bit microcontroller. You need a memory device (an SD card for example) and an MP3 chipset. See this article for example. You can find many more at avrfreaks. There you could also find articles for playing sounds without an external chip.

You can play basic sounds with Pulse-width modulation (PWM), but for a real song you would need a DAC. I have seen projects that played MP3 files using only a DAC and software, but they involved the more powerful ARM microcontrollers.

网友答案:

If you're feeling particularly creative, you can build your own digital-to-analog converter using a resistor ladder.

网友答案:

If you're using an Arduino, you can buy Lady Ada's WaveShield for $22 usd. Lady Ada offers many Arduino goodies worth purchasing. Some examples are GPS, Ethernet and stepper/servo shields.

网友答案:

You could take a look at the open source MP3 player and see how they do it. My guess is that you'll need a D/A converter to produce sound of a decent quality.

网友答案:

You don't have enough space on the ATMega128 to do anything too fancy. The easiest way to connect the speaker (small 2" or less) is via a resistor. Check the current sinking capacity of the output and calculate R accordingly.

---------------------- +V
    |
    \
    / R
    \
    /              ----------
    |              |
    |   ------     |
    ----|    |-----| Microcontroller
        /    \     |
       --------    |
        Speaker    ---------

As regards to producing tones, basic switching of the output will produce basic stylaphone sounding crapolla. You can use pulse width modulation to produce approximations of any analog sound (too complex to go into here and the AtMega probably won't have enough puff or storage). This was the method used to make audio drivers for PCs with no sound card (only the built in speaker) in the good ol' days...

分享给朋友:
您可能感兴趣的文章:
随机阅读: