|
【米尔-STM32MP257开发板试用体验】基于ALSA采集音频到WAV文件
[复制链接]
本帖最后由 qinyunti 于 2025-4-10 16:42 编辑
鎾斁鍣ㄥ姞杞藉け璐�: 鏈娴嬪埌Flash Player锛岃鍒� 瀹夎
7edf9b496398a6cdd0c5a9a470bf557d
b站视频
- 前言
前面我们进行了alsa音频应用开发测试,以及基于speex的音频回声消除和降噪算法测试,接下来将两者结合起来,实现实时对讲的Demo。先要来实现基于alsa的音频播放与采集。前一篇已经实现了wav音频播放,本文来实现基于alsa录音到wav文件。
- 实现
新建alsa_rec_wav.c文件。
2.1 wav 解析
Wav格式解析参考公众号文章
https://mp.weixin.qq.com/s/Lmf1rAi-U4B-ZbZbMsuwqA
Wav解析与添加头代码如下
- #include <stdint.h>
- #include <stdio.h>
- #include <string.h>
-
- #define CHUNK_RIFF "RIFF"
- #define CHUNK_WAVE "WAVE"
- #define CHUNK_FMT "fmt "
- #define CHUNK_DATA "data"
-
- typedef struct
- {
- uint32_t off;
- uint32_t chunksize;
- uint16_t audioformat;
- uint16_t numchannels;
- uint32_t samplerate;
- uint32_t byterate;
- uint16_t blockalign;
- uint16_t bitspersample;
- uint32_t datasize;
- }wav_t;
-
- static int wav_decode_head(uint8_t* buffer, wav_t* wav)
- {
- uint8_t* p = buffer;
- uint32_t chunksize;
- uint32_t subchunksize;
- if(0 != memcmp(p,CHUNK_RIFF,4))
- {
- return -1;
- }
- p += 4;
- chunksize = (uint32_t)p[0] | ((uint32_t)p[1]<<8) | ((uint32_t)p[2]<<16) | ((uint32_t)p[3]<<24);
- wav->chunksize = chunksize;
- p += 4;
- if(0 != memcmp(p,CHUNK_WAVE,4))
- {
- return -2;
- }
- p += 4;
-
- do
- {
- if(0 == memcmp(p,CHUNK_FMT,4))
- {
- p += 4;
- subchunksize = (uint32_t)p[0] | ((uint32_t)p[1]<<8) | ((uint32_t)p[2]<<16) | ((uint32_t)p[3]<<24);
- p += 4;
-
- wav->audioformat = (uint16_t)p[0] | ((uint16_t)p[1]<<8);
- if((wav->audioformat == 0x0001) || (wav->audioformat == 0xFFFE))
- {
- p += 2;
- wav->numchannels = (uint16_t)p[0] | ((uint16_t)p[1]<<8);
- p += 2;
- wav->samplerate = (uint32_t)p[0] | ((uint32_t)p[1]<<8) | ((uint32_t)p[2]<<16) | ((uint32_t)p[3]<<24);
- p += 4;
- wav->byterate = (uint32_t)p[0] | ((uint32_t)p[1]<<8) | ((uint32_t)p[2]<<16) | ((uint32_t)p[3]<<24);
- p += 4;
- wav->blockalign = (uint16_t)p[0] | ((uint16_t)p[1]<<8);
- p += 2;
- wav ->bitspersample = (uint16_t)p[0] | ((uint16_t)p[1]<<8);
- p += 2;
-
- if(subchunksize >16)
- {
-
- uint16_t cbsize = (uint16_t)p[0] | ((uint16_t)p[1]<<8);
- p += 2;
- if(cbsize > 0)
- {
-
- p += 2;
- p += 4;
-
- p += 16;
- }
- }
- }
- else
- {
- p += subchunksize;
- }
- }
- else if(0 == memcmp(p,CHUNK_DATA,4))
- {
- p += 4;
- subchunksize = (uint32_t)p[0] | ((uint32_t)p[1]<<8) | ((uint32_t)p[2]<<16) | ((uint32_t)p[3]<<24);
- wav->datasize = subchunksize;
- p += 4;
- wav->off = (uint32_t)(p- buffer);
- return 0;
- }
- else
- {
- p += 4;
- subchunksize = (uint32_t)p[0] | ((uint32_t)p[1]<<8) | ((uint32_t)p[2]<<16) | ((uint32_t)p[3]<<24);
- p += 4;
- p += subchunksize;
- }
- }while((uint32_t)(p - buffer) < (chunksize + 8));
- return -3;
- }
-
-
- static void wav_fill_head(uint8_t* buffer, int samples, int chnum, int freq)
- {
-
-
- uint32_t chunksize = 44-8+samples*chnum*16/8;
- uint8_t* p = (uint8_t*)buffer;
- uint32_t bps = freq*chnum*16/8;
- uint32_t datalen = samples*chnum*16/8;
- p[0] = 'R';
- p[1] = 'I';
- p[2] = 'F';
- p[3] = 'F';
- p[4] = chunksize & 0xFF;
- p[5] = (chunksize>>8) & 0xFF;
- p[6] = (chunksize>>16) & 0xFF;
- p[7] = (chunksize>>24) & 0xFF;
- p[8] = 'W';
- p[9] = 'A';
- p[10] = 'V';
- p[11] = 'E';
-
- p[12] = 'f';
- p[13] = 'm';
- p[14] = 't';
- p[15] = ' ';
-
- p[16] = 16;
- p[17] = 0;
- p[18] = 0;
- p[19] = 0;
-
- p[20] = 1;
- p[21] = 0;
-
- p[22] = chnum;
- p[23] = 0;
-
- p[24] = freq & 0xFF;
- p[25] = (freq>>8) & 0xFF;
- p[26] = (freq>>16) & 0xFF;
- p[27] = (freq>>24) & 0xFF;
-
- p[28] = bps & 0xFF;
- p[29] = (bps>>8) & 0xFF;
- p[30] = (bps>>16) & 0xFF;
- p[31] = (bps>>24) & 0xFF;
-
- p[32] = chnum*16/8;
- p[33] = 0;
-
- p[34] = 16;
- p[35] = 0;
-
- p[36] = 'd';
- p[37] = 'a';
- p[38] = 't';
- p[39] = 'a';
-
- p[40] = datalen & 0xFF;
- p[41] = (datalen>>8) & 0xFF;
- p[42] = (datalen>>16) & 0xFF;
- p[43] = (datalen>>24) & 0xFF;
- }
-
- void wav_print(wav_t* wav)
- {
- printf("off:%d\r\n",wav->off);
- printf("chunksize:%d\r\n",wav->chunksize);
- printf("audioformat:%d\r\n",wav->audioformat);
- printf("numchannels:%d\r\n",wav->numchannels);
- printf("samplerate:%d\r\n",wav->samplerate);
- printf("byterate:%d\r\n",wav->byterate);
- printf("blockalign:%d\r\n",wav->blockalign);
- printf("bitspersample:%d\r\n",wav->bitspersample);
- printf("datasize:%d\r\n",wav->datasize);
- }
-
2.2 alsa采集音频
参考
https://www.alsa-project.org/alsa-doc/alsa-lib/_2test_2pcm_8c-example.html
打开设备与参数初始化
- int alsa_init(char* device, snd_pcm_t **handle, int ch, int freq)
- {
- int err;
-
- if ((err = snd_pcm_open(handle, device, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
- printf("Playback open error: %s\n", snd_strerror(err));
- return -1;
- }
-
- snd_pcm_sframes_t frames;
- if ((err = snd_pcm_set_params(*handle,
- SND_PCM_FORMAT_S16_LE,
- SND_PCM_ACCESS_RW_INTERLEAVED,
- ch,
- freq,
- 0,
- 100000)) < 0) {
- printf("Playback open error: %s\n", snd_strerror(err));
- snd_pcm_close(*handle);
- return -2;
- }
-
- snd_pcm_hw_params_t *hwparams = NULL;
- snd_pcm_hw_params_malloc(&hwparams);
- snd_pcm_hw_params_any(*handle, hwparams);
- snd_pcm_hw_params_set_access(*handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
- snd_pcm_hw_params_set_format(*handle, hwparams,SND_PCM_FORMAT_S16_LE);
- snd_pcm_hw_params_set_channels(*handle, hwparams,ch);
- snd_pcm_hw_params_set_rate(*handle, hwparams,freq,0);
- snd_pcm_hw_params_set_period_size(*handle, hwparams, NN, 0);
- snd_pcm_hw_params_set_buffer_size(*handle, hwparams, 4*NN);
- snd_pcm_hw_params(*handle, hwparams);
- snd_pcm_hw_params_free(hwparams);
-
- snd_pcm_start(*handle);
- return 0;
- }
采集
- int alsa_rec(snd_pcm_t *handle, void* buffer, size_t size)
- {
- int timeout = 0;
- snd_pcm_sframes_t frames;
- do{
- frames = snd_pcm_avail_update(handle);
- if(frames < size){
- usleep(1000);
- timeout++;
- if(timeout >= 1000){
- printf("rec timeout\n");
- return -1;
- }
- }
- }while(frames < size);
-
- frames = snd_pcm_readi(handle, buffer, (snd_pcm_uframes_t)size);
- if (frames < 0){
- frames = snd_pcm_recover(handle, frames, 0);
-
- printf("snd_pcm_recover: %s\n", snd_strerror(frames));
- }
- if (frames < 0) {
- printf("snd_pcm_writei failed: %s\n", snd_strerror(frames));
- return -1;
- }
- if (frames > 0 && frames < (long)sizeof(buffer)){
- printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), frames);
- return -2;
- }
- return 0;
- }
-
完成关闭
- int alsa_deinit(snd_pcm_t *handle)
- {
- int err;
-
- err = snd_pcm_drain(handle);
- if (err < 0){
- printf("snd_pcm_drain failed: %s\n", snd_strerror(err));
- }
- snd_pcm_close(handle);
- return 0;
- }
2.3 读取音频写入wav文件
- #include <alsa/asoundlib.h>
- #define NN 128
- #define CH_MAX 2
-
- int main(int argc, char* argv[])
- {
- snd_pcm_t *handle = NULL;
- int res;
- FILE *wav_fd;
- int16_t wav_buf[NN*CH_MAX];
- uint8_t wav_head_buf[44];
- wav_t wav;
- int ch;
- int samps;
- int times;
- int sampleRate;
-
- if(argc != 6){
- printf("usage:alsa_rec_wav dev wav ch freq samps\r\n");
- return -1;
- }
- ch = atoi(argv[3]);
- sampleRate = atoi(argv[4]);
- samps = atoi(argv[5]);
-
- printf("ch=%d,freq=%d,samps=%d\r\n",ch,sampleRate,samps);
- wav_fd = fopen(argv[2], "wb+");
- if(wav_fd < 0){
- printf("open file %s err\r\n",argv[2]);
- return -2;
- }
-
- times = samps / NN;
-
- printf("fill head\r\n");
- wav_fill_head(wav_head_buf, times*NN, ch, sampleRate);
- if(44 != fwrite(wav_head_buf, 1, 44, wav_fd)){
- printf("write file %s err\n",argv[2]);
- fclose(wav_fd);
- return -3;
- }
-
- printf("alsa_init\r\n");
- res = alsa_init(argv[1], &handle, ch, sampleRate);
- if(res != 0){
- fclose(wav_fd);
- printf("alsa_init err\r\n");
- return -4;
- }
-
- printf("alsa_rec\r\n");
- for(int i=0; i<times; i++)
- {
- if(alsa_rec(handle, wav_buf, NN) < 0){
- printf("rec %s err\r\n");
- fclose(wav_fd);
- alsa_deinit(handle);
- return -5;
- }
-
- if(NN*ch != fwrite(wav_buf, sizeof(int16_t), NN*ch, wav_fd)){
- printf("write file %s err\r\n",argv[2]);
- fclose(wav_fd);
- alsa_deinit(handle);
- return -6;
- }
- }
-
- fclose(wav_fd);
- alsa_deinit(handle);
- return 0;
- }
-
- 测试
编译
source /opt/st/myd-ld25x/4.2.4-snapshot/environment-setup-cortexa35-ostl-linux
$CC alsa_rec_wav.c -o alsa_rec_wav -lasound
导出到windows下
cp alsa_rec_wav /mnt/d
导入到开发板
chmod +x alsa_rec_wav
测试
先按照单通道和双通道录音
./alsa_rec_wav default rec1.wav 1 16000 128000
./alsa_rec_wav default rec2.wav 2 16000 128000
然后播放
./alsa_play_wav default rec1.wav
./alsa_play_wav default rec2.wav
- 总结
以上实现了录音到wav文件,已经实现播放与采集,后面就可以实现语音对讲并添加实时音频处理算法实现对讲回声消除与降噪的Demo。
|
|