#include "play.h"
Play::Play(QObject *parent) : QObject(parent)
{
}
int Play::snd_pcm_init()
{
snd_pcm_hw_params_t *hwparams = NULL;
int ret;
/* 打开PCM设备 */
ret = snd_pcm_open(&pcm, PCM_PLAYBACK_DEV, SND_PCM_STREAM_PLAYBACK, 0);
if (0 > ret) {
fprintf(stderr, "snd_pcm_open error: %s: %s\n",
PCM_PLAYBACK_DEV, snd_strerror(ret));
return -1;
}
/* 实例化hwparams对象 */
snd_pcm_hw_params_malloc(&hwparams);
/* 获取PCM设备当前硬件配置,对hwparams进行初始化 */
ret = snd_pcm_hw_params_any(pcm, hwparams);
if (0 > ret) {
fprintf(stderr, "snd_pcm_hw_params_any error: %s\n", snd_strerror(ret));
snd_pcm_hw_params_free(hwparams); //释放内存
snd_pcm_close(pcm);
return -1;
}
/**************
设置参数
***************/
/* 设置访问类型: 交错模式 */
ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
if (0 > ret) {
fprintf(stderr, "snd_pcm_hw_params_set_access error: %s\n", snd_strerror(ret));
snd_pcm_hw_params_free(hwparams); //释放内存
snd_pcm_close(pcm);
return -1;
}
/* 设置数据格式: 有符号16位、小端模式 */
ret = snd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S16_LE);
if (0 > ret) {
fprintf(stderr, "snd_pcm_hw_params_set_format error: %s\n", snd_strerror(ret));
snd_pcm_hw_params_free(hwparams); //释放内存
snd_pcm_close(pcm);
return -1;
}
/* 设置采样率 */
ret = snd_pcm_hw_params_set_rate_near(pcm, hwparams, &wav_fmt.SampleRate, 0);
if (0 > ret) {
fprintf(stderr, "snd_pcm_hw_params_set_rate error: %s\n", snd_strerror(ret));
snd_pcm_hw_params_free(hwparams); //释放内存
snd_pcm_close(pcm);
return -1;
}
/* 设置声道数: 双声道 */
ret = snd_pcm_hw_params_set_channels(pcm, hwparams, wav_fmt.NumChannels);
if (0 > ret) {
fprintf(stderr, "snd_pcm_hw_params_set_channels error: %s\n", snd_strerror(ret));
snd_pcm_hw_params_free(hwparams); //释放内存
snd_pcm_close(pcm);
return -1;
}
snd_pcm_uframes_t frames_per_buffer = 0;
snd_pcm_hw_params_get_buffer_size_max(hwparams, &frames_per_buffer);
ret = snd_pcm_hw_params_set_buffer_size_near(pcm, hwparams, &frames_per_buffer);
if(ret < 0)
{
qDebug() << "error";
}
period_size = frames_per_buffer / 4;
/* 设置周期大小: period_size */
ret = snd_pcm_hw_params_set_period_size(pcm, hwparams, period_size, 0);
if (0 > ret) {
fprintf(stderr, "snd_pcm_hw_params_set_period_size error: %s\n", snd_strerror(ret));
snd_pcm_hw_params_free(hwparams); //释放内存
snd_pcm_close(pcm);
return -1;
}
/* 设置周期数(驱动层buffer的大小): periods */
ret = snd_pcm_hw_params_set_periods(pcm, hwparams, periods, 0);
if (0 > ret) {
fprintf(stderr, "snd_pcm_hw_params_set_periods error: %s\n", snd_strerror(ret));
snd_pcm_hw_params_free(hwparams); //释放内存
snd_pcm_close(pcm);
return -1;
}
/* 使配置生效 */
ret = snd_pcm_hw_params(pcm, hwparams);
snd_pcm_hw_params_free(hwparams); //释放hwparams对象占用的内存
if (0 > ret) {
fprintf(stderr, "snd_pcm_hw_params error: %s\n", snd_strerror(ret));
snd_pcm_close(pcm);
return -1;
}
snd_pcm_hw_params_get_period_size(hwparams,&period_size, 0);
qDebug() << period_size << " " << frames_per_buffer;
buf_bytes = period_size * wav_fmt.BlockAlign; //变量赋值,一个周期的字节大小
return 0;
}
int Play::open_wav_file(const char *file)
{
RIFF_t wav_riff;
DATA_t wav_data;
int ret;
fd = open(file, O_RDONLY);
if (0 > fd) {
fprintf(stderr, "open error: %s: %s\n", file, strerror(errno));
return -1;
}
/* 读取RIFF chunk */
ret = read(fd, &wav_riff, sizeof(RIFF_t));
if (sizeof(RIFF_t) != ret) {
if (0 > ret)
perror("read error");
else
fprintf(stderr, "check error: %s\n", file);
close(fd);
return -1;
}
if (strncmp("RIFF", wav_riff.ChunkID, 4) ||//校验
strncmp("WAVE", wav_riff.Format, 4))
{
fprintf(stderr, "check error: %s\n", file);
close(fd);
return -1;
}
/* 读取sub-chunk-fmt */
ret = read(fd, &wav_fmt, sizeof(FMT_t));
if (sizeof(FMT_t) != ret) {
if (0 > ret)
perror("read error");
else
fprintf(stderr, "check error: %s\n", file);
close(fd);
return -1;
}
if (strncmp("fmt ", wav_fmt.Subchunk1ID, 4)) {//校验
fprintf(stderr, "check error: %s\n", file);
close(fd);
return -1;
}
/* 打印音频文件的信息 */
printf("<<<<音频文件格式信息>>>>\n\n");
printf(" file name: %s\n", file);
printf(" Subchunk1Size: %u\n", wav_fmt.Subchunk1Size);
printf(" AudioFormat: %u\n", wav_fmt.AudioFormat);
printf(" NumChannels: %u\n", wav_fmt.NumChannels);
printf(" SampleRate: %u\n", wav_fmt.SampleRate);
printf(" ByteRate: %u\n", wav_fmt.ByteRate);
printf(" BlockAlign: %u\n", wav_fmt.BlockAlign);
printf(" BitsPerSample: %u\n\n", wav_fmt.BitsPerSample);
/* sub-chunk-data */
if (0 > lseek(fd, sizeof(RIFF_t) + 8 + wav_fmt.Subchunk1Size,
SEEK_SET)) {
perror("lseek error");
close(fd);
return -1;
}
while(sizeof(DATA_t) == read(fd, &wav_data, sizeof(DATA_t)))
{
/* 找到sub-chunk-data */
if (!strncmp("data", wav_data.Subchunk2ID, 4))//校验
return 0;
if (0 > lseek(fd, wav_data.Subchunk2Size, SEEK_CUR)) {
perror("lseek error");
close(fd);
return -1;
}
}
fprintf(stderr, "check error: %s\n", file);
return -1;
}
void Play::paly_wav(const char *file)
{
open_wav_file(file);
snd_pcm_init();
int err;
char *buf = (char *)malloc(buf_bytes);
if (NULL == buf)
{
perror("malloc error");
close(fd);
snd_pcm_close(pcm);
return ;
}
unsigned long size = 0;
while (1)
{
memset(buf, 0x00, buf_bytes); //buf清零
int ret = read(fd, buf, buf_bytes); //从音频文件中读取数据
if (0 >= ret) // 如果读取出错或文件读取完毕
{
close(fd);
//snd_pcm_drop(pcm);
snd_pcm_drain(pcm);
snd_pcm_close(pcm);
if(ret != 0)
{
qDebug() << "error";
}
break;
}
size += ret;
qDebug() << "size=" << size;
ret = snd_pcm_writei(pcm, buf, period_size);
if (ret < 0)
{
/*
if ((err = snd_pcm_prepare (pcm)) < 0)
{
fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
snd_strerror (err));
close(fd);
snd_pcm_close(pcm);
return ;
}
else*/
{
fprintf(stderr, "snd_pcm_writei error: %s %---d\n", snd_strerror(ret), ret);
close(fd);
snd_pcm_close(pcm);
}
return ;
}
else if (ret < period_size)
{
//实际写入的帧数小于指定的帧数
//此时我们需要调整下音频文件的读位置
//将读位置向后移动(往回移)(period_size-ret)*frame_bytes个字节
//frame_bytes表示一帧的字节大小
if (0 > lseek(fd, (ret-period_size) * wav_fmt.BlockAlign, SEEK_CUR))
{
perror("lseek error");