voice_broadcast/audio_play.c

165 lines
4.7 KiB
C
Raw Normal View History

2025-06-03 12:49:29 +08:00
#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;
}