#ifndef _PCM_MNGR_H_
#define _PCM_MNGR_H_
#include <alsa/asoundlib.h>
#include <vector>
class WaveFile;
class PcmMngr
{
private:
PcmMngr();
public:
~PcmMngr();
static PcmMngr *Instance();
public:
bool open();
bool close();
bool fast_play(const WaveFile &wav);//播放一个文件
bool fast_play(const WaveFile &wav, uint32_t time, uint32_t interval_ms = 0);//一个文件播放多次
bool fast_play(const std::vector<WaveFile *> &wav_vec, uint32_t interval_ms = 0);//连续播放多个文件
private:
bool fast_set_pcm_params(const WaveFile &wav);
private:
snd_pcm_t *m_handle;
};
#endif
#include "PcmMngr.h"
#include "WaveFile.h"
#include <unistd.h>
PcmMngr::PcmMngr()
: m_handle(nullptr)
{
}
PcmMngr::~PcmMngr()
{
}
PcmMngr *PcmMngr::Instance()
{
static PcmMngr obj;
return &obj;
}
bool PcmMngr::open()
{
int ret = snd_pcm_open(&m_handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (ret < 0)
{
printf("Playback open error: %s\n", snd_strerror(ret));
return false;
}
return true;
}
bool PcmMngr::close()
{
if (m_handle)
{
snd_pcm_close(m_handle);
m_handle = nullptr;
}
return true;
}
bool PcmMngr::fast_set_pcm_params(const WaveFile &wav)
{
snd_pcm_format_t format;
switch (wav.get_sample_length()) {
case 16:
format = SND_PCM_FORMAT_S16_LE;
break;
case 8:
format = SND_PCM_FORMAT_U8;
break;
default:
format = SND_PCM_FORMAT_UNKNOWN;
break;
}
int err;
if (err = snd_pcm_set_params(m_handle, format, SND_PCM_ACCESS_RW_INTERLEAVED, wav.get_channels(), wav.get_sample_rate(), 1, 500) < 0)
{
printf("Playback open error: %s\n", snd_strerror(err));
return false;
}
return true;
}
/*
异步播放:先读文件,再播放
*/
bool PcmMngr::fast_play(const WaveFile &wav)
{
if (!wav.is_ok())
{
printf("wav file invalid \n");
return false;
}
if (!fast_set_pcm_params(wav))
{
printf("fast_set_pcm_params failed \n");
return false;
}
const char *data = wav.get_data();
uint32_t data_len = wav.get_data_len();
uint32_t frame_size = wav.get_frame_size();
if (!data || data_len <= 0 || frame_size <= 0)
{
return false;
}
int ret = 0;
snd_pcm_sframes_t frames = 0;
uint32_t tmpDataLen = data_len;
uint32_t frame_cnt = data_len / frame_size; //剩余要播放的帧数
const char *ptr = data;//数据偏移指针
do
{
frames = snd_pcm_writei(m_handle, ptr, frame_cnt);//将交错帧写入PCM,返回实际写入PCM的帧数,失败时返回错误码
if (frames < 0)
{
if (ret = snd_pcm_recover(m_handle, frames, 0) < 0) //从错误中恢复流状态或暂停
{
printf("snd_pcm_writei failed: %s\n", snd_strerror(ret));
break;
}
}
else
{
frame_cnt -= frames;
ptr += frame_size * frames;
tmpDataLen -= frame_size * frames;
}
} while (tmpDataLen > 0);
//停止PCM保存待处理的帧
/*
ret = snd_pcm_drain(m_handle);
if (ret < 0)
{
printf("snd_pcm_drain failed: %s\n", snd_strerror(ret));
}
*/
return true;
}
bool PcmMngr::fast_play(const WaveFile &wav, uint32_t time, uint32_t interval_ms)
{
for (uint32_t i = 0; i < time; i++)
{
fast_play(wav);
usleep(interval_ms*1000);
}
return true;
}
bool PcmMngr::fast_play(const std::vector<WaveFile *> &wav_vec, uint32_t interval_ms)
{
for (WaveFile *wav : wav_vec)
{
fast_play(*wav);
usleep(interval_ms*1000);
}
return true;
}