ARM技术论坛
直播中

jf_67024233

2年用户 303经验值
擅长:电源/新能源 模拟技术 测量仪表 接口/总线/驱动
私信 关注

【米尔-STM32MP257开发板试用体验】米尔-STM32MP257开发板上alsa的wav音频播放测试

今天对收到的米尔-STM32MP257开发板做音频播放测试,是实现基于alsa的音频播放与采集。本文来实现基于alsa播放wav格式pcm音频。

8.jpg

9.jpg

  1. 实现

    新建alsa_play_wav.c文件。
    2.1 wav 解析

    Wav解析与添加头代码如下

    #include <stdint.h>
    #include <stdio.h>
    #include <string.h>
    /* WAV解析 */
    #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)
                    {                    /* 有ext区域 */
                        uint16_t cbsize = (uint16_t)p[0] | ((uint16_t)p[1]<<8);
                        p += 2;
                        if(cbsize > 0)
                        {                        /* ext数据 2字节有效bits wValidBitsPerSample ,4字节dwChannelMask 16字节SubFormat */
                            p += 2;
                            p += 4;
                            /* 比对subformat */
                            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;
    }/* 填充44字节的wav头 */
    static void wav_fill_head(uint8_t* buffer, int samples, int chnum, int freq)
    {    /*
         * 添加wav头信息
         */
        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;  /* Subchunk1Size */
        p[17] = 0;
        p[18] = 0;
        p[19] = 0;
        p[20] = 1;  /* PCM */
        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;      /* ByteRate */
        p[29] = (bps>>8) & 0xFF;
        p[30] = (bps>>16) & 0xFF;
        p[31] = (bps>>24) & 0xFF; 
        p[32] = chnum*16/8; /* BlockAlign */
        p[33] = 0;
        p[34] = 16;  /* BitsPerSample */
        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播放

    打开设备与参数初始化

    int alsa_init(char* device, snd_pcm_t **handle, wav_t* wav)
    {    int err;
        if ((err = snd_pcm_open(handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {  /* 最后一个参数0 阻塞模式 1非阻塞模式 */
            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,                    wav->numchannels,                    wav->samplerate,                    0,
                        100000)) < 0) {   /* 0.5sec */
            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,wav->numchannels);
    	snd_pcm_hw_params_set_rate(*handle, hwparams,wav->samplerate,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);
        return 0;
    }
    

    播放:

    int alsa_play(snd_pcm_t *handle, const void* buffer, size_t size)
    {    snd_pcm_sframes_t frames;    frames = snd_pcm_writei(handle, buffer, (snd_pcm_uframes_t)size);  /* 注意这里的size为frames即点数 而不是字节大小 */
        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;
        /* pass the remaining samples, otherwise they're dropped in close */
        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 MAX_CH 2
    int main(int argc, char* argv[])
    {    snd_pcm_t *handle = NULL;    int res;
        FILE *wav_fd;    int16_t wav_buf[NN*MAX_CH];    uint8_t wav_head_buf[128]; /* 输入wav文件头缓存 */
        wav_t wav;    int samps;  /* 采样点数 */
        int times;    /* 读取次数 */
        int sampleRate;
        if(argc != 3){
            printf("usage:alsa_play_wav dev wav\\r\\n");
            return -1;
        }    wav_fd = fopen(argv[2], "rb");
        if(wav_fd < 0){
            printf("open file %s err\\r\\n",argv[2]);
            return -2;
        }    if(sizeof(wav_head_buf) != fread(wav_head_buf, 1, sizeof(wav_head_buf), wav_fd)){
            printf("read file %s err\\r\\n",argv[2]);
            fclose(wav_fd);        return -3;
        }    if(0 != (res=wav_decode_head(wav_head_buf, &wav))){
            printf("decode file %s err %d\\r\\n",argv[2],res);
            fclose(wav_fd);        return -4;
        }    printf("[wav]\\r\\n");
        wav_print(&wav);    samps = wav.datasize;    samps /= wav.blockalign;  /* 采样点数 =  数据大小 除以 blockalign */
        printf("\\r\\nsamps:%d\\r\\n",samps);
        sampleRate = wav.samplerate;    res = alsa_init(argv[1], &handle, &wav);
        if(res != 0){
            fclose(wav_fd);        printf("alsa_init err\\r\\n");
            return -5;
        }    times = samps / NN;   /* 一次读取NN个点,读取times次 */
        fseek(wav_fd,wav.off,SEEK_SET);    for(int i=0; i<times; i++)
        {       if(NN*wav.numchannels != fread(wav_buf, sizeof(int16_t), NN*wav.numchannels, wav_fd)){
                printf("read file %s err\\r\\n",argv[2]);
                fclose(wav_fd);            alsa_deinit(handle);            return -6;
           }       if(alsa_play(handle, wav_buf, NN) < 0){
            printf("play %s err\\r\\n");
            fclose(wav_fd);        alsa_deinit(handle);        return -7;
           }    }    fclose(wav_fd);    alsa_deinit(handle);    return 0;
    }
    
  2. 测试

    编译

    source /opt/st/myd-ld25x/4.2.4-snapshot/environment-setup-cortexa35-ostl-linux

    $CC alsa_play_wav.c -o alsa_play_wav -lasound

    导出到windows下

    cp alsa_play_wav /mnt/d

    导入到开发板
    0001.png

chmod +x alsa_play_wav

准备wav文件

下载一个mp3音乐,使用ffmpeg转为wav

.\ffmpeg-7.1.1-full_build\bin\ffmpeg.exe -i zdcxf.mp3 -ac 1 -ar 16000 -ss 0 -t 20 sp1.wav

.\ffmpeg-7.1.1-full_build\bin\ffmpeg.exe -i zdcxf.mp3 -ac 2 -ar 16000 -ss 0 -t 20 sp2.wav

导入测试wav文件
0002.png

  1. 测试

    ./alsa_play_wav default sp1.wav

    ./alsa_play_wav default sp2.wav

aa658ca82eec1834f52482d2a4828ad5.jpg

  1. 总结

米尔-STM32MP257开发板按以上代码就可以实现了播放wav音频。(文中测试代码来自网络)

更多回帖

发帖
×
20
完善资料,
赚取积分