上一个帖子写了如何播放wave文件, 除了用已有资源之外, 还可以自己生成wave文件来播放, 因为wave文件很简单, 就是一个文件头, 剩下的都是PCM数据.
这个帖子写写怎么生成wave文件, 或者干脆不要文件头, 直接生成播放内容.
生成wave文件很多种语言都有library与API, 这里为求简单, 自己定义一个文件头格式, 整个生成就是一个单cpp文件.
这里是一个wave文件头的参考: http://www.topherlee.com/software/pcm-tut-wavformat.html
根据此文件设计文件头结构体如下:
复制代码
根据要生成的音频的采样率,声道,位长填写此结构体, 并放在wave文件的前面, 后面跟上PCM数据, 那么这个wave文件就做成了. 可以在任何设备的播放器播放以验证效果, 也可以按照上一个帖子的方法下载到板子上去播放.
先来生成一个600Hz的纯正弦波信号看看. 整个cpp代码也就这么点:
复制代码
使用VC的命令行工具编译成执行文件并运行,一切无误的话会在当前文件夹生成名为TestWav16bit_2ch_22K5_Sine600.wav的文件,用任何播放器均可播放,下面是它的形状:
将生成数据的代码那里改成这样就成生成同频率的方波:
复制代码
同样编译运行,注意改生成文件名字. 这是生成文件的形状:
如果你试着比较听一下, 600Hz的正弦波明显比同频率方波听起来柔和一点.
这两个文件都可以直接下载到板子上进行播放, 注意改文件后缀名与文件大小定义即可.详情请参阅第一篇帖子.
这里把生成的两个wave文件附上来以作参考:
当然修改算法, 可以生成各种各样的波形, 如果感兴趣, 可以自己试试改一改, 听一听.
比如把生成数据的代码改成:
复制代码
那么生成的就是伪随机数据, 听起来就是噪音.
接下来用板子直接生成波形播放,直接生成跟上面的上位机程序基本上一样, 只是把前面header丢掉直接播放内容即可, 但是官方例程里面buffer操作写的有些晦涩, 看起来不容易明白.
他buffer操作这样的:
首先填写完一个整burfer,开始DMA播放
DMA完成一半的时候,这时候要填写第一半已经播放完的buffer,
DMA完成后填写后面一半这时候因为DMA设置成Cicular模式,所以播放没有停顿
最后数据不足以填写一半buffer的时候又从头开始取数据
把上面生成数据的代码复制在第一次填写buffer与每次半填写buffer的地方就可以了:
复制代码
复制代码
感兴趣的可以对比一下,声音效果跟电脑上生成的wave文件烧写进去应该是一样的.
后续有心得再来发帖.
这个帖子写写怎么生成wave文件, 或者干脆不要文件头, 直接生成播放内容.
生成wave文件很多种语言都有library与API, 这里为求简单, 自己定义一个文件头格式, 整个生成就是一个单cpp文件.
这里是一个wave文件头的参考: http://www.topherlee.com/software/pcm-tut-wavformat.html
根据此文件设计文件头结构体如下:
- struct SimpleWavHdr
- {
- FOURCC RiffHdr; //"RIFF"
-
- uint32_t ChunkSize; //file size - 8
- FOURCC WavHdr;//"WAVE"
- FOURCC FmtHdr; //"fmt "
- uint32_t HdrLen; //16, length of above
- uint16_t DataType; //1->PCM
- uint16_t ChanNo; //1 Channel
-
- uint32_t SampleRate;//8000 Hz
- uint32_t SamplePerSec; //8000 sample per second
-
- uint16_t BytePerSample;//Bytes per sample
- uint16_t BitsPerSample;//Bits per sample
-
- FOURCC dataHdr;//"data"
-
- uint32_t RawSize;//data size from this point
- };
先来生成一个600Hz的纯正弦波信号看看. 整个cpp代码也就这么点:
- #include <process.h>
- #include <windows.h>
- #include <mmsystem.h>
- #include <cstdint>
- #include <iostream>
- #include <vector>
- #define _USE_MATH_DEFINES
- #ifdef _MATH_DEFINES_DEFINED
- #undef _MATH_DEFINES_DEFINED
- #endif
- #include <math.h>
- #pragma comment(lib, "Winmm.lib")
- #pragma comment(lib, "user32.lib")
- using namespace std;
- #ifndef M_PI
- #define M_PI 3.14159265358979323846
- #endif
- struct SimpleWavHdr
- {
- FOURCC RiffHdr; //"RIFF"
-
- uint32_t ChunkSize; //file size - 8
- FOURCC WavHdr;//"WAVE"
- FOURCC FmtHdr; //"fmt "
- uint32_t HdrLen; //16, length of above
- uint16_t DataType; //1->PCM
- uint16_t ChanNo; //1 Channel
-
- uint32_t SampleRate;//8000 Hz
- uint32_t SamplePerSec; //8000 sample per second
-
- uint16_t BytePerSample;//Bytes per sample
- uint16_t BitsPerSample;//Bits per sample
-
- FOURCC dataHdr;//"data"
-
- uint32_t RawSize;//data size from this point
- };
- void WinWavPlay(void);
- #define TEST_SAMPLE_RATE 22050
- #define TEST_SAMPLE_LEN_SEC 2
- #define TEST_SAMPLE_NUM (TEST_SAMPLE_RATE*TEST_SAMPLE_LEN_SEC)
- #define CHAN_NO 2
- #define SAMPLE_SIZE_IN_BYTE_CH_MONO 2
- #define SAMPLE_SIZE_IN_BYTE_ALL_CH (SAMPLE_SIZE_IN_BYTE_CH_MONO * CHAN_NO)
- #define BITS_PER_BYTE 8
- #define SAMPLE_SIZE_IN_BITS_CH_MONO (SAMPLE_SIZE_IN_BYTE_CH_MONO * BITS_PER_BYTE)
- #define SAMPLE_SIZE_IN_BITS_ALL_CH (SAMPLE_SIZE_IN_BITS_CH_MONO * CHAN_NO)
- #define AUDIO_HZ 600
- #define AUDIO_CYCLE (TEST_SAMPLE_RATE/AUDIO_HZ)
- #define UINT8_HIGH (UINT8_MAX/2)
- #define UINT8_LOW 0
- #define UINT16_HIGH (UINT16_MAX/2)
- #define UINT16_LOW 0
- #define INT16_HIGH (INT16_MAX/2)
- #define INT16_LOW (0-INT16_HIGH)
- const char TEST_WAV_NAME[]= "TestWav16bit_2ch_22K5_Sine600.wav";
- HANDLE hData = NULL; // handle of waveform data memory
- HPSTR lpData = NULL; // pointer to waveform data memory
- SimpleWavHdr wavHdr;
- int main(int argc, char** argv)
- {
- FILE* fp = NULL;
- ZeroMemory( &wavHdr, sizeof(wavHdr));
-
- //Fill the header of the wave file
- wavHdr.RiffHdr = FOURCC_RIFF;
- wavHdr.ChunkSize = sizeof(wavHdr)-8 + SAMPLE_SIZE_IN_BYTE_ALL_CH * TEST_SAMPLE_NUM;
- wavHdr.WavHdr = mmioFOURCC('W','A','V','E');
- wavHdr.FmtHdr = mmioFOURCC('f','m','t',' ');
- wavHdr.HdrLen = 16;
- wavHdr.DataType = WAVE_FORMAT_PCM;
- wavHdr.ChanNo = CHAN_NO;
- wavHdr.SampleRate = TEST_SAMPLE_RATE;
- wavHdr.SamplePerSec = TEST_SAMPLE_RATE * SAMPLE_SIZE_IN_BYTE_ALL_CH;
- wavHdr.BytePerSample = SAMPLE_SIZE_IN_BYTE_ALL_CH;
- wavHdr.BitsPerSample = SAMPLE_SIZE_IN_BITS_CH_MONO;
- wavHdr.dataHdr = mmioFOURCC('d','a','t','a');
- wavHdr.RawSize = SAMPLE_SIZE_IN_BYTE_ALL_CH * TEST_SAMPLE_NUM;
- fp = fopen(TEST_WAV_NAME, "wb");
- fwrite(&wavHdr, sizeof(wavHdr), 1, fp);
- uint8_t testByte;
- uint16_t test_16bit_2ch[2];
- //Generate and save the content
- for(auto i=0; i<TEST_SAMPLE_NUM; ++i)
- {
- //Pure Sine Wave
- test_16bit_2ch[0] = (uint16_t)((INT16_MAX)*(sin(M_PI*2*(i%AUDIO_CYCLE*1.0)/AUDIO_CYCLE)));
- test_16bit_2ch[1] = test_16bit_2ch[0];
-
- //(uint16_t) ((i%AUDIO_CYCLE)>(AUDIO_CYCLE/2))?INT16_HIGH:INT16_LOW;;
- //some decay effect
- // test_16bit_2ch[0] = test_16bit_2ch[0] * (i+1)/TEST_SAMPLE_NUM;
- //test_16bit_2ch[1] = test_16bit_2ch[1] * (i+1)/TEST_SAMPLE_NUM;
- fwrite((const void*)&test_16bit_2ch, sizeof(test_16bit_2ch[0]), 2, fp);
- }
- fclose(fp);
- //Call this function to play it with windows to verify the content
- //WinWavPlay();
- return 0;
- }
将生成数据的代码那里改成这样就成生成同频率的方波:
- //Square
- test_16bit_2ch[0] = (uint16_t) ((i%AUDIO_CYCLE)>(AUDIO_CYCLE/2))?INT16_HIGH:INT16_LOW;;
- test_16bit_2ch[1] = test_16bit_2ch[0];
如果你试着比较听一下, 600Hz的正弦波明显比同频率方波听起来柔和一点.
这两个文件都可以直接下载到板子上进行播放, 注意改文件后缀名与文件大小定义即可.详情请参阅第一篇帖子.
这里把生成的两个wave文件附上来以作参考:
当然修改算法, 可以生成各种各样的波形, 如果感兴趣, 可以自己试试改一改, 听一听.
比如把生成数据的代码改成:
- //Random noise
- test_16bit_2ch[0] = (uint16_t) rand();
- test_16bit_2ch[1] = test_16bit_2ch[0];
接下来用板子直接生成波形播放,直接生成跟上面的上位机程序基本上一样, 只是把前面header丢掉直接播放内容即可, 但是官方例程里面buffer操作写的有些晦涩, 看起来不容易明白.
他buffer操作这样的:
首先填写完一个整burfer,开始DMA播放
DMA完成一半的时候,这时候要填写第一半已经播放完的buffer,
DMA完成后填写后面一半这时候因为DMA设置成Cicular模式,所以播放没有停顿
最后数据不足以填写一半buffer的时候又从头开始取数据
把上面生成数据的代码复制在第一次填写buffer与每次半填写buffer的地方就可以了:
- /* Initialize the data buffer */
- for(PlaybackPosition=PLAY_HEADER;
- PlaybackPosition < (PLAY_HEADER+PLAY_BUFF_SIZE);
- PlaybackPosition+=2)
- {
- test_x_index = (PlaybackPosition-PLAY_HEADER)/2;
- // PlayBuff[PlaybackPosition]=*((__IO uint16_t *)(AUDIO_FILE_ADDRESS + PlaybackPosition));
- if(0==(PlaybackPosition%2))
- {
- //First Chanel
- #ifdef TEST_NOISE
- //Noise
- test_16bit_data = (uint16_t) rand();
- #endif
-
- #ifdef TEST_SINE
- //Sine 600Hz
- test_16bit_data = (uint16_t)((INT16_MAX)*(sin(M_PI*2*(test_x_index%AUDIO_CYCLE*1.0)/AUDIO_CYCLE)));
- #endif
-
- #ifdef TEST_SQUARE
- //Square 600Hz
- test_16bit_data = (uint16_t) ((test_x_index%AUDIO_CYCLE)>(AUDIO_CYCLE/2))?INT16_HIGH:INT16_LOW;;
- #endif
-
- PlayBuff[PlaybackPosition] = test_16bit_data;
- }
- else
- {
- //Second Chanel
- PlayBuff[PlaybackPosition] = test_16bit_data;
- }
- }
- /* Upate the first or the second part of the buffer */
- for(int i = 0; i < PLAY_BUFF_SIZE/2; i++)
- {
- // PlayBuff[i+position] = *(uint16_t *)(AUDIO_FILE_ADDRESS + PlaybackPosition);
- test_x_index = (PlaybackPosition-PLAY_HEADER)/2;
- // PlayBuff[PlaybackPosition]=*((__IO uint16_t *)(AUDIO_FILE_ADDRESS + PlaybackPosition));
- if(0==(PlaybackPosition%2))
- {
- //First Chanel
- #ifdef TEST_NOISE
- //Noise
- test_16bit_data = (uint16_t) rand();
- #endif
-
- #ifdef TEST_SINE
- //Sine 600Hz
- test_16bit_data = (uint16_t)((INT16_MAX)*(sin(M_PI*2*(test_x_index%AUDIO_CYCLE*1.0)/AUDIO_CYCLE)));
- #endif
-
- #ifdef TEST_SQUARE
- //Square 600Hz
- test_16bit_data = (uint16_t) ((test_x_index%AUDIO_CYCLE)>(AUDIO_CYCLE/2))?INT16_HIGH:INT16_LOW;;
- #endif
-
- PlayBuff[i+position] = test_16bit_data;
- }
- else
- {
- //Second Chanel
- PlayBuff[i+position] = test_16bit_data;
- }
-
- PlaybackPosition+=2;
- }
后续有心得再来发帖.