ARM嵌入式板 C++使用 Alsa 播放音频文件的部分内容
·
其他Alsa相关博客:
交叉编译AlsaLib和AlsaUtils_libalsa交叉编译-CSDN博客
嵌入式板子使用Alsa接口和C++程序,录制Wav音频文件-CSDN博客
嵌入式板子使用Alsa接口和C++程序,播放Wav音频文件-CSDN博客
函数原型
void play_selected_audio(const char *filename, int start_time, int duration)
参数:
filename : WAV文件名
start_time : 播放开始位置(秒数)
duration :播放音频数据长度(秒数)
如下调用意思为,播放test.wav文件,从第10秒开始播放,播放5秒钟的音频
play_selected_audio("test.wav", 10, 5);
流程图:

关键点是:
算出开始播放位置对应的偏移和需要播放的数据量大小。
后续的循环就读取这范围内的数据播放即可。
WAV头部结构体:
/* sizeof(WAV_FMT) = 12 */
typedef struct WAV_RIFF
{
char ChunkID[4]; /* "RIFF" */
uint32_t ChunkSize; /* 从下一个地址开始到文件末尾的总字节数 */
char Format[4]; /* "WAVE" */
} __attribute__ ((packed)) RIFF_t;
/* sizeof(WAV_FMT) = 24 */
typedef struct WAV_FMT
{
char Subchunk1ID[4]; /* "fmt " */
uint32_t Subchunk1Size; /* 16 for PCM */
uint16_t AudioFormat; /* PCM = 1*/
uint16_t NumChannels; /* Mono = 1, Stereo = 2, etc. */
uint32_t SampleRate; /* 8000, 44100, etc. */
uint32_t ByteRate; /* = SampleRate * NumChannels * BitsPerSample/8 */
uint16_t BlockAlign; /* = NumChannels * BitsPerSample/8 */
uint16_t BitsPerSample; /* 8bits, 16bits, etc. */
} __attribute__ ((packed)) FMT_t;
typedef struct WAV_DATA
{
char Subchunk2ID[4]; /* "data" */
uint32_t Subchunk2Size; /* data size */
} __attribute__ ((packed)) DATA_t;
typedef struct WavHeaderSt
{
RIFF_t wavRiffSt;
FMT_t wavFmtSt;
WAV_DATA wavDataSt;
} WavHeader;
源代码:
// 从 WAV 文件中选取指定时间段的数据并播放
void play_selected_audio(const char *filename, int start_time, int duration)
{
int fd = open(filename, O_RDONLY);
if (fd < 0) {
perror("Failed to open file");
return;
}
WavHeader header;
if (read(fd, &header, sizeof(WavHeader)) != sizeof(WavHeader)) {
perror("Failed to read WAV header");
close(fd);
return;
}
// 检查是否为有效的 WAV 文件
if (strncmp(header.wavRiffSt.ChunkID, "RIFF", 4) != 0 || strncmp(header.wavRiffSt.Format, "WAVE", 4) != 0) {
fprintf(stderr, "Not a valid WAV file\n");
close(fd);
return;
}
/*
每秒钟音频所含的数据(单位:字节)
SampleRate:是音频的采样率,指的是每秒对音频信号进行采样的次数,采样率越高,音频的细节就越丰富,声音还原就越准确。
NumChannels:代表音频的声道数。常见的有单声道(num_channels为 1)和立体声(num_channels为 2)。
如果是单声道,每个采样点只包含一个声道的音频信息;如果是立体声,则包含左声道和右声道两个声道的音频信息。
BitsPerSample:是每个采样点所占用的位数,用来表示每个采样值的精度。
比如,常见的有 8 位、16 位、24 位等。8 位表示每个采样点可以用 256 个不同的值来表示,16 位则可以表示 65536 个不同的值,位数越高,音频的动态范围就越大,声音的质量也就越高。
*/
long dataBytes_Per_Second = header.wavFmtSt.SampleRate * header.wavFmtSt.NumChannels * (header.wavFmtSt.BitsPerSample / 8);
// 计算起始偏移量
int start_offset = start_time * dataBytes_Per_Second;
// 计算要读取的数据大小
int data_size = duration * dataBytes_Per_Second;
printf("data_size = %ld, duration = %ld, sample_rate = %ld, bits_per_sample= %ld\n",
data_size, duration, header.wavFmtSt.SampleRate, header.wavFmtSt.BitsPerSample);
// 定位到起始位置(从头开始便宜)
if (lseek(fd, sizeof(WavHeader) + start_offset, SEEK_SET) == -1)
{
perror("Failed to seek to start position");
close(fd);
return;
}
// 初始化 ALSA 设备
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;
if (snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0)
{
perror("Failed to open PCM device");
close(fd);
return;
}
int dir;
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_hw_params_any(handle, params);
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_channels(handle, params, header.wavFmtSt.NumChannels);
snd_pcm_hw_params_set_rate_near(handle, params, &header.wavFmtSt.SampleRate, &dir);
if (snd_pcm_hw_params(handle, params) < 0)
{
perror("Failed to set PCM hardware parameters");
snd_pcm_close(handle);
close(fd);
return;
}
// 分块读取并播放数据
const int buffer_size = 4096; // 每次读取的块大小
char *buffer = (char *)malloc(buffer_size);
if (buffer == NULL)
{
perror("Failed to allocate memory");
snd_pcm_close(handle);
close(fd);
return;
}
int remaining = data_size;
while (remaining > 0)
{
int read_size = (remaining < buffer_size) ? remaining : buffer_size;
if (read(fd, buffer, read_size) != read_size)
{
perror("Failed to read data");
break;
}
snd_pcm_sframes_t frames_written = snd_pcm_writei(handle, buffer, read_size / header.wavFmtSt.BlockAlign);
if (frames_written < 0)
{
frames_written = snd_pcm_recover(handle, frames_written, 0);
if (frames_written < 0)
{
perror("Failed to write data to PCM device");
break;
}
}
remaining -= read_size;
}
// 清理资源
free(buffer);
snd_pcm_drain(handle);
snd_pcm_close(handle);
close(fd);
}
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)