voice_broadcast/audio_play.c
2025-06-03 04:49:29 +00:00

165 lines
4.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
void check_and_set_max_volume()
{
FILE *fp;
char buffer[256];
int volume_left = -1;
int speaker_on = 0;
// 检查 PCM 音量
fp = popen("amixer get PCM | grep 'Front Left'", "r");
if (fp)
{
while (fgets(buffer, sizeof(buffer), fp))
{
if (sscanf(buffer, " Front Left: %d", &volume_left) == 1)
{
break;
}
}
pclose(fp);
}
if (volume_left == -1)
{
printf("无法获取音量信息\n");
}
else if (volume_left < 192)
{
printf("当前音量为 %d设置为最大...\n", volume_left);
system("amixer set PCM 192");
}
else
{
printf("音量已是最大(%d\n", volume_left);
}
// 检查扬声器是否开启
fp = popen("amixer get Speaker | grep 'Mono:'", "r");
if (fp)
{
while (fgets(buffer, sizeof(buffer), fp))
{
if (strstr(buffer, "[on]"))
{
speaker_on = 1;
break;
}
}
pclose(fp);
}
if (!speaker_on)
{
printf("扬声器未打开,正在开启...\n");
system("amixer set Speaker unmute");
}
else
{
printf("扬声器已开启\n");
}
}
int main(int argc, char *argv[])
{
check_and_set_max_volume();
const char *device = "hw:0,0"; // 要使用的 ALSA 设备
const char *filename = "001.wav"; // WAV 文件路径
snd_pcm_t *pcm_handle;
snd_pcm_hw_params_t *params;
unsigned int sample_rate = 44100; // 采样率(需与 WAV 文件一致)
int pcm, dir;
snd_pcm_uframes_t frames;
FILE *file;
char *buffer;
int buffer_size;
// 打开音频文件
file = fopen(filename, "rb");
if (!file)
{
perror("Unable to open audio file");
return 1;
}
// 跳过标准 WAV 头部44 字节),如果你的音频文件已经是纯 PCM 数据可以注释掉此行
fseek(file, 44, SEEK_SET);
// 打开 PCM 设备
if ((pcm = snd_pcm_open(&pcm_handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
{
fprintf(stderr, "Unable to open PCM device %s: %s\n", device, snd_strerror(pcm));
fclose(file);
return 1;
}
// 分配并初始化硬件参数对象
snd_pcm_hw_params_alloca(&params);
snd_pcm_hw_params_any(pcm_handle, params);
snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(pcm_handle, params, SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_channels(pcm_handle, params, 2);
snd_pcm_hw_params_set_rate_near(pcm_handle, params, &sample_rate, &dir);
// 应用硬件参数
if ((pcm = snd_pcm_hw_params(pcm_handle, params)) < 0)
{
fprintf(stderr, "Unable to set hardware parameters: %s\n", snd_strerror(pcm));
snd_pcm_close(pcm_handle);
fclose(file);
return 1;
}
// 获取每个周期period的帧数并据此计算缓冲区大小
snd_pcm_hw_params_get_period_size(params, &frames, &dir);
buffer_size = frames * 2 /*bytes per sample*/ * 2 /*channels*/;
buffer = (char *)malloc(buffer_size);
if (!buffer)
{
fprintf(stderr, "Unable to allocate buffer\n");
snd_pcm_close(pcm_handle);
fclose(file);
return 1;
}
// 无限循环播放
while (1)
{
size_t bytes_read = fread(buffer, 1, buffer_size, file);
if (bytes_read == 0)
{
// 已经读到文件末尾,重新回到数据区的起始位置(跳过头部)
fseek(file, 44, SEEK_SET);
continue;
}
// 计算本次要写入的“帧”数,如果最后一块数据不足一个完整周期,按实际读到的字节数计算
snd_pcm_uframes_t frames_to_deliver = bytes_read / (2 * 2);
// 写入 PCM
int ret = snd_pcm_writei(pcm_handle, buffer, frames_to_deliver);
if (ret == -EPIPE)
{
// 缓冲区下溢,重启 PCM 设备
snd_pcm_prepare(pcm_handle);
}
else if (ret < 0)
{
fprintf(stderr, "Playback error: %s\n", snd_strerror(ret));
}
// 如果写入的帧数小于需要的,可以在这里处理(可选)
}
// 下面的代码理论上永远不会执行到,但为了完整性保留
free(buffer);
snd_pcm_drain(pcm_handle);
snd_pcm_close(pcm_handle);
fclose(file);
return 0;
}