系列文章目录
第一章 使用SDL2库播放pcm数据音频
文章目录
前言
主要阐述如何使用SDL2库播放pcm原始数据音频,且如何实现多个音频混合播放。
一、实现播放的流程
1.初始化音频子系统及退出接口
//初始化音频子系统
SDL_Init(SDL_INIT_AUDIO))
//退出
SDL_Quit()
2.音频设备打开及关闭
SDL_AudioSpec spec, spec_out;
SDL_AudioDeviceID m_dAudioDev;
spec.freq = m_nOutSampleRate;
spec.format = m_nOutSampleFmt;
spec.channels = m_nOutChannels;
spec.silence = 0; //0代表最大声音
spec.samples = 256;
spec.callback = &StaticReadAudioData; //音频回调函数,由于本实例使用c++实现故需传入类实例参数然后再去调用类函数实现
spec.userdata = this;
//打开音频设备
//第一个参数表示默认音频解码设备
//第二个参数,如果为 1,则表示打开音频设备用于录音;如果为 0,则表示打开音频设备用于播放
//第三个参数表示音频输入参数,第四个表示音频输出参数
//第五个参数用于指定允许哪些音频特性发生变化,比如SDL_AUDIO_ALLOW_FREQUENCY_CHANGE允许频率变化,SDL_AUDIO_ALLOW_SAMPLES_CHANGE允许采样样本数变化
m_dAudioDev = SDL_OpenAudioDevice(NULL, 0, &spec, &spec_out, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_SAMPLES_CHANGE)
/关闭音频设备
SDL_CloseAudioDevice(m_dAudioDev);
//音频回调的执行函数
void AudioPcm::ReadAudioData(unsigned char *stream, int len)
{
SDL_memset(stream, 0, len);//必须执行,否则回调时会一直发声
SDL_MixAudioFormat(stream, m_aAudioPlaying[i].pPlayPos, m_nOutSampleFmt, len, m_aAudioPlaying[i].nPlayVolume);//音频数据播放
m_aAudioPlaying[i].pPlayPos += len;
m_aAudioPlaying[i].nPlayLen -= len;
}
static void StaticReadAudioData(void *udata, unsigned char *stream, int len)
{
AudioPcm *ap = static_cast<AudioPcm *>(udata);
ap->ReadAudioData(stream, len);
}
3.音频数据流创建与推送
SDL_AudioStream *audioStream;
//新建音频流,实际音频参数可以重采样成目标音频参数,也即打开音频设备时配置的参数
audioStream = SDL_NewAudioStream(m_nSampleFmt, m_nChannels, m_nSampleRate, m_nOutSampleFmt, m_nOutChannels, m_nOutSampleRate);
//对应释放接口
SDL_FreeAudioStream(audioStream);
//判断音频流缓冲中是否有数据,返回值为数据字节数
SDL_AudioStreamAvailable(audioStream);
//将音频流缓冲中数据拷贝到read_data缓冲中,read_data指针用于回调函数中使用
SDL_AudioStreamGet(audioStream, read_data, max_play_data_len);
m_aAudioPlaying[m_nCurAudioIndex].pPlayPos = (uint8_t *)read_data;
m_aAudioPlaying[m_nCurAudioIndex].nPlayLen = ret; //长度为读出数据长度,在read_audio_data中做减法
//等待回调函数中数据播放完毕
while (1) //判断是否播放完毕
{
if (m_aAudioPlaying[m_nCurAudioIndex].nPlayLen == 0)
{
timeout_count = 0;
SDL_AudioStreamFlush(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
break;
}
if (timeout_count >= 2000)
{
timeout_count = 0;
break;
}
timeout_count++;
SDL_Delay(1);
}
//音频数据推送接口
SDL_AudioStreamPut(audioStream, data, len)
二、如何实现多路音频播放
1.创建多个音频流并推送
使用多线程结合上述“音频数据流创建与推送”章节实现多路音频流创建和推送
2.音频设备打开的回调函数中多次播放音频流
实现多路音频播放的关键点就在于在回调函数中多次调用SDL_MixAudioFormat接口播放各路音频数据
三、具体代码实例
为方便理解SDL2库相关接口,为避免重复制造轮子,现将代码粘贴共享如下。
1.头文件代码
#pragma once
#include <stdio.h>
#include <stdint.h>
#include <thread>
#include <mutex>
#include <memory>
#ifdef __linux
#include <SDL2/SDL_types.h>
#include <SDL2/SDL.h>
using namespace std;
#define AUDIO_PLAYING_MAX 4
typedef struct AudioPlaying {
uint8_t *pPlayPos;
uint32_t nPlayLen;
int nPlayVolume; // 0<= nPlayVolume <= SDL_MIX_MAXVOLUME
SDL_AudioStream *audioStream;
bool bUsed;
std::mutex mutexAudioPlay;
}AudioPlaying_t;
class AudioPcm {
public:
AudioPcm();
~AudioPcm();
AudioPcm(int sampleRate, SDL_AudioFormat sampleFmt, int channels);
AudioPcm(int sampleRate, SDL_AudioFormat sampleFmt, int channels, int resampleRate);
AudioPcm(int sampleRate, SDL_AudioFormat sampleFmt, int channels, int resampleRate, SDL_AudioFormat outSampleFmt);
public:
// 0<=volume<=SDL_MIX_MAXVOLUME=128
void SetVolume(int audioIndex, int volume);
void SetSampleRate(int sampleRate);
void SetSampleRateAndStart(int sampleRate);
void SetSampleRateAndStart(int iSrcSampleRate, int iOutSampleRate);
void SetSampleFmt(SDL_AudioFormat sampleFmt);
void SetChannels(int channels);
int GetSampleRate(void);
SDL_AudioFormat GetSampleFmt(void);
int GetChannels(void);
int GetOutSampleRate(void);
SDL_AudioFormat GetOutSampleFmt(void);
int GetOutChannels(void);
int GetVolume(int audioIndex);
int RequestAudioPlay();
void SetAudioPlayData(int index, uint8_t *audioPlayPos, uint32_t audioPlayLen);
uint32_t GetAudioPlayLen(int index);
void SetAudioPlayUsed(int index, bool used);
bool GetAudioPlayUsed(int index);
void SetAudioPlayStream(int index, SDL_AudioStream *audioStream);
SDL_AudioStream *GetAudioPlayStream(int index);
int AudioClearData();
int AudioPushData(char *data, int len);
int AudioOpenDevice(int index);
void AudioCloseDevice();
void AudioThreadStart();
void AudioThreadStop();
void AudioThreadFunc();
bool AudioThreadStatus();
static int AudioPlayByFile(const char *filename, int sampleRate, SDL_AudioFormat sampleFmt = AUDIO_S16SYS, int channels = 1);
static int AudioPlayData(char *data, int len, int sampleRate, SDL_AudioFormat sampleFmt = AUDIO_S16SYS, int channels = 1);
static int AudioPlayStop(int audioIndex);
static std::shared_ptr<AudioPcm> CreateInstanse(int sampleRate, SDL_AudioFormat sampleFmt, int channels);
static std::shared_ptr<AudioPcm> GetInstanse();
static void DeleteInstanse();
void ReadAudioData(unsigned char *stream, int len);
private:
int AudioDataFinished();
private:
//最多同时播放4个
AudioPlaying_t m_aAudioPlaying[AUDIO_PLAYING_MAX];
int m_nSampleRate;
int m_nOutSampleRate;
SDL_AudioFormat m_nSampleFmt;
SDL_AudioFormat m_nOutSampleFmt;
int m_nChannels;
int m_nOutChannels;
int m_nNbSamples;
bool m_bAudioThreadStart;
SDL_Thread * m_threadAudio;
pthread_attr_t m_attrAudio;
int m_nCurAudioIndex;
std::mutex m_mutexAudioStream;
SDL_AudioDeviceID m_dAudioDev;
std::mutex m_mutexAudioDev;
};
#endif
2.源文件代码
#ifdef __linux
#include <unistd.h>
#include <iostream>
#include <signal.h>
#include "audio_pcm.h"
std::shared_ptr<AudioPcm> s_pAudioPlay = nullptr;
std::shared_ptr<AudioPcm> AudioPcm::CreateInstanse(int sampleRate, SDL_AudioFormat sampleFmt, int channels)
{
if (s_pAudioPlay == nullptr)
{
s_pAudioPlay = std::make_shared<AudioPcm>(sampleRate, sampleFmt, channels);
s_pAudioPlay->SetSampleRate(sampleRate);
}
else
{
if (sampleRate != s_pAudioPlay->GetSampleRate() || sampleFmt != s_pAudioPlay->GetSampleFmt() || channels != s_pAudioPlay->GetChannels())
{
s_pAudioPlay->SetSampleFmt(sampleFmt);
s_pAudioPlay->SetChannels(channels);
s_pAudioPlay->SetSampleRate(sampleRate);
}
}
return s_pAudioPlay;
}
std::shared_ptr<AudioPcm> AudioPcm::GetInstanse()
{
return s_pAudioPlay;
}
void AudioPcm::DeleteInstanse()
{
if (s_pAudioPlay != nullptr)
{
s_pAudioPlay = nullptr;
}
}
AudioPcm::AudioPcm(int sampleRate, SDL_AudioFormat sampleFmt, int channels)
: m_nSampleRate(sampleRate)
, m_nSampleFmt(sampleFmt)
, m_nChannels(channels)
{
m_nOutSampleFmt = AUDIO_F32SYS;
m_nNbSamples = 1;
m_dAudioDev = 0;
if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
printf("Could not initialize SDL - %s\n", SDL_GetError());
}
m_bAudioThreadStart = false;
for (int i=0; i<AUDIO_PLAYING_MAX; i++)
{
m_aAudioPlaying[i].nPlayVolume = 50;
m_aAudioPlaying[i].nPlayLen = 0;
m_aAudioPlaying[i].pPlayPos = NULL;
m_aAudioPlaying[i].audioStream = NULL;
m_aAudioPlaying[i].bUsed = false;
}
//SetSampleRateAndStart(sampleRate);
}
AudioPcm::AudioPcm(int sampleRate, SDL_AudioFormat sampleFmt, int channels, int resampleRate, SDL_AudioFormat outSampleFmt)
: m_nSampleRate(sampleRate)
, m_nSampleFmt(sampleFmt)
, m_nChannels(channels)
{
m_nOutSampleFmt = outSampleFmt;
m_nNbSamples = 1;
m_dAudioDev = 0;
if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
printf("Could not initialize SDL - %s\n", SDL_GetError());
}
m_bAudioThreadStart = false;
for (int i=0; i<AUDIO_PLAYING_MAX; i++)
{
m_aAudioPlaying[i].nPlayVolume = 50;
m_aAudioPlaying[i].nPlayLen = 0;
m_aAudioPlaying[i].pPlayPos = NULL;
m_aAudioPlaying[i].audioStream = NULL;
m_aAudioPlaying[i].bUsed = false;
}
//SetSampleRateAndStart(sampleRate, resampleRate);
}
AudioPcm::AudioPcm(int sampleRate, SDL_AudioFormat sampleFmt, int channels, int resampleRate)
: m_nSampleRate(sampleRate)
, m_nSampleFmt(sampleFmt)
, m_nChannels(channels)
{
m_nOutSampleFmt = AUDIO_F32SYS;
m_nNbSamples = 1;
m_dAudioDev = 0;
if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
printf("Could not initialize SDL - %s\n", SDL_GetError());
}
cout << "=====" << __func__ << "(), line:" << __LINE__ << "\n";
m_bAudioThreadStart = false;
for (int i=0; i<AUDIO_PLAYING_MAX; i++)
{
m_aAudioPlaying[i].nPlayVolume = 50;
m_aAudioPlaying[i].nPlayLen = 0;
m_aAudioPlaying[i].pPlayPos = NULL;
m_aAudioPlaying[i].audioStream = NULL;
m_aAudioPlaying[i].bUsed = false;
}
//SetSampleRateAndStart(sampleRate, resampleRate);
}
AudioPcm::~AudioPcm()
{
cout << "=====" << __func__ << "(), line:" << __LINE__ << "\n";
AudioThreadStop();
SDL_Quit();
}
void AudioPcm::SetVolume(int audioIndex, int volume)
{
if (audioIndex >= AUDIO_PLAYING_MAX)
{
return;
}
std::unique_lock<std::mutex> locker(m_aAudioPlaying[audioIndex].mutexAudioPlay);
m_aAudioPlaying[audioIndex].nPlayVolume = volume;
}
void AudioPcm::SetSampleRate(int sampleRate)
{
cout << "=====" << __func__ << "(), line:" << __LINE__ << ", sampleRate:" << sampleRate << "\n";
m_nSampleRate = sampleRate;
m_nOutSampleRate = sampleRate;
m_nOutChannels = m_nChannels;
}
void AudioPcm::SetSampleRateAndStart(int sampleRate)
{
m_nSampleRate = sampleRate;
m_nOutSampleRate = sampleRate;
m_nOutChannels = m_nChannels;
cout << "=====" << __func__ << "(), line:" << __LINE__ << ", sampleRate:" << sampleRate << "\n";
AudioThreadStop();
AudioThreadStart();
}
void AudioPcm::SetSampleRateAndStart(int iSrcSampleRate, int iOutSampleRate)
{
m_nSampleRate = iSrcSampleRate;
m_nOutSampleRate = iOutSampleRate;
m_nOutChannels = m_nChannels;
cout << "=====" << __func__ << "(), line:" << __LINE__ << ", iSrcSampleRate:" << iSrcSampleRate << ", iOutSampleRate:" << iOutSampleRate << "\n";
AudioThreadStop();
AudioThreadStart();
}
void AudioPcm::SetSampleFmt(SDL_AudioFormat sampleFmt)
{
m_nSampleFmt = sampleFmt;
}
void AudioPcm::SetChannels(int channels)
{
m_nChannels = channels;
}
int AudioPcm::GetSampleRate(void)
{
return m_nSampleRate;
}
SDL_AudioFormat AudioPcm::GetSampleFmt(void)
{
return m_nSampleFmt;
}
int AudioPcm::GetChannels(void)
{
return m_nChannels;
}
int AudioPcm::GetOutSampleRate(void)
{
return m_nOutSampleRate;
}
SDL_AudioFormat AudioPcm::GetOutSampleFmt(void)
{
return m_nOutSampleFmt;
}
int AudioPcm::GetOutChannels(void)
{
return m_nOutChannels;
}
int AudioPcm::GetVolume(int audioIndex)
{
if (audioIndex >= AUDIO_PLAYING_MAX)
{
return -1;
}
std::unique_lock<std::mutex> locker(m_aAudioPlaying[audioIndex].mutexAudioPlay);
return m_aAudioPlaying[audioIndex].nPlayVolume;
}
int AudioPcm::RequestAudioPlay()
{
int index = -1;
//0固定被占用,所以从1开始
for (int i=1; i<AUDIO_PLAYING_MAX; i++)
{
m_aAudioPlaying[i].mutexAudioPlay.lock();
if (!m_aAudioPlaying[i].bUsed)
{
m_aAudioPlaying[i].nPlayLen = 0;
m_aAudioPlaying[i].bUsed = true;
index = i;
m_aAudioPlaying[i].mutexAudioPlay.unlock();
break;
}
m_aAudioPlaying[i].mutexAudioPlay.unlock();
}
return index;
}
void AudioPcm::SetAudioPlayData(int index, uint8_t *audioPlayPos, uint32_t audioPlayLen)
{
if (index >= AUDIO_PLAYING_MAX)
{
return;
}
std::unique_lock<std::mutex> locker(m_aAudioPlaying[index].mutexAudioPlay);
m_aAudioPlaying[index].pPlayPos = audioPlayPos;
m_aAudioPlaying[index].nPlayLen = audioPlayLen;
}
uint32_t AudioPcm::GetAudioPlayLen(int index)
{
if (index >= AUDIO_PLAYING_MAX)
{
return 0;
}
std::unique_lock<std::mutex> locker(m_aAudioPlaying[index].mutexAudioPlay);
return m_aAudioPlaying[index].nPlayLen;
}
void AudioPcm::SetAudioPlayUsed(int index, bool used)
{
if (index >= AUDIO_PLAYING_MAX)
{
return;
}
std::unique_lock<std::mutex> locker(m_aAudioPlaying[index].mutexAudioPlay);
m_aAudioPlaying[index].bUsed = used;
}
bool AudioPcm::GetAudioPlayUsed(int index)
{
if (index >= AUDIO_PLAYING_MAX)
{
return true;
}
std::unique_lock<std::mutex> locker(m_aAudioPlaying[index].mutexAudioPlay);
return m_aAudioPlaying[index].bUsed;
}
void AudioPcm::SetAudioPlayStream(int index, SDL_AudioStream *audioStream)
{
if (index >= AUDIO_PLAYING_MAX)
{
return;
}
std::unique_lock<std::mutex> locker(m_aAudioPlaying[index].mutexAudioPlay);
m_aAudioPlaying[index].audioStream = audioStream;
}
SDL_AudioStream *AudioPcm::GetAudioPlayStream(int index)
{
if (index >= AUDIO_PLAYING_MAX)
{
return NULL;
}
std::unique_lock<std::mutex> locker(m_aAudioPlaying[index].mutexAudioPlay);
return m_aAudioPlaying[index].audioStream;
}
static void StaticReadAudioData(void *udata, unsigned char *stream, int len)
{
AudioPcm *ap = static_cast<AudioPcm *>(udata);
ap->ReadAudioData(stream, len);
}
//回调函数,音频设备需要更多数据的时候会调用该回调函数
void AudioPcm::ReadAudioData(unsigned char *stream, int len)
{
SDL_memset(stream, 0, len);//必须执行,否则回调时会一直发声
cout << "==1===ReadAudioData callback" << endl;
for (int i=0; i<AUDIO_PLAYING_MAX; i++)
{
std::unique_lock<std::mutex> locker(m_aAudioPlaying[i].mutexAudioPlay);
if (m_aAudioPlaying[i].bUsed && m_aAudioPlaying[i].nPlayLen > 0)
{
len = (len > m_aAudioPlaying[i].nPlayLen ? m_aAudioPlaying[i].nPlayLen : len);
cout << "==2===m_aAudioPlaying[" << i << "].nPlayLen:" << m_aAudioPlaying[i].nPlayLen << ", len:" << len << endl;
SDL_MixAudioFormat(stream, m_aAudioPlaying[i].pPlayPos, m_nOutSampleFmt, len, m_aAudioPlaying[i].nPlayVolume);
m_aAudioPlaying[i].pPlayPos += len;
m_aAudioPlaying[i].nPlayLen -= len;
//if (m_aAudioPlaying[i].nPlayLen == 0)
//{
//}
}
}
}
static int StaticAudioThreadFunc(void *arg)
{
AudioPcm *ap = static_cast<AudioPcm *>(arg);
ap->AudioThreadFunc();
return 0;
}
int AudioPcm::AudioOpenDevice(int index)
{
SDL_AudioSpec spec, spec_out;
bool usb_audio_flag = false;
cout << __func__ << "(), line:" << __LINE__ << endl;
std::unique_lock<std::mutex> locker(m_mutexAudioDev);
//存在则无需再打开
if (index != 0 && m_dAudioDev != 0)
{
return 0;
}
else if (index == 0 && m_dAudioDev > 0)
{
for (int i=0; i<AUDIO_PLAYING_MAX; i++)
{
std::unique_lock<std::mutex> locker(m_aAudioPlaying[i].mutexAudioPlay);
m_aAudioPlaying[i].nPlayLen = 0;
m_aAudioPlaying[i].pPlayPos = NULL;
/*if (m_aAudioPlaying[i].audioStream)
{
SDL_FreeAudioStream(m_aAudioPlaying[i].audioStream);
m_aAudioPlaying[i].audioStream = NULL;
}*/
}
SDL_PauseAudioDevice(m_dAudioDev, 1);
SDL_CloseAudioDevice(m_dAudioDev);
m_dAudioDev = 0;
}
cout << "=====m_nOutSampleRate:" << m_nOutSampleRate << ", m_nSampleRate:" << m_nSampleRate << ", m_nSampleFmt:" << m_nSampleFmt
<< ", m_nOutSampleFmt:" << m_nOutSampleFmt << ", m_nChannels:" << m_nChannels << ", m_nOutChannels:" << m_nOutChannels << ", index:" << index << endl;
spec.freq = m_nOutSampleRate;
spec.format = m_nOutSampleFmt;
spec.channels = m_nOutChannels;
spec.silence = 0;
if (index)
{
spec.samples = 256;
}
else
{
spec.samples = (int)std::ceil((float)m_nSampleRate/1000.0)*16;//防止值过小导致无法播放,过大又导致播放卡顿,频率越大样本需要越大
}
spec.callback = &StaticReadAudioData;
spec.userdata = this;
int n=SDL_GetNumAudioDevices(0);
for (int i=0; i<n; i++)
{
const char *device_name = SDL_GetAudioDeviceName(i, 0);
if (device_name == NULL)
{
cout << i << ", Error:" << SDL_GetError() << "\n";
}
else
{
cout << i << ",device_name:" << device_name << "\n";
if (strcmp("USB audio CODEC, USB Audio", device_name) == 0 || strcmp("PCM2912A Audio Codec Analog Stereo", device_name) == 0)
{
if ((m_dAudioDev = SDL_OpenAudioDevice(device_name, 0, &spec, &spec_out, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_SAMPLES_CHANGE))==0) {
cout << "can't open audio, " << SDL_GetError() << endl;
return -1;
}
usb_audio_flag = true;
break;
}
}
}
if (!usb_audio_flag)
{
if ((m_dAudioDev = SDL_OpenAudioDevice(NULL, 0, &spec, &spec_out, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_SAMPLES_CHANGE))==0) {
cout << "can't open audio, " << SDL_GetError() << endl;
return -1;
}
}
if (spec_out.format != m_nOutSampleFmt)
{
cout << "spec_out.format:" << spec_out.format << " error\n";
SDL_CloseAudioDevice(m_dAudioDev);
return -2;
}
if (spec_out.channels != m_nOutChannels)
{
cout << "channels error\n";
SDL_CloseAudioDevice(m_dAudioDev);
return -2;
}
m_nOutSampleRate = spec_out.freq;
m_nNbSamples = spec.samples;
cout << "m_nSampleRate:" << m_nSampleRate << ", m_nOutSampleRate:" << m_nOutSampleRate << ", m_nNbSamples:" << m_nNbSamples << "\n";
//播放
SDL_PauseAudioDevice(m_dAudioDev, 0);
return 0;
}
void AudioPcm::AudioCloseDevice()
{
cout << __func__ << "(), line:" << __LINE__ << endl;
for (int i=0; i<AUDIO_PLAYING_MAX; i++)
{
m_aAudioPlaying[i].mutexAudioPlay.lock();
//存在音频正在使用则不关闭设备
if (m_aAudioPlaying[i].bUsed)
{
m_aAudioPlaying[i].mutexAudioPlay.unlock();
return;
}
m_aAudioPlaying[i].mutexAudioPlay.unlock();
}
std::unique_lock<std::mutex> locker(m_mutexAudioDev);
cout << __func__ << "(), line:" << __LINE__ << endl;
if (m_dAudioDev > 0)
{
SDL_PauseAudioDevice(m_dAudioDev, 1);
SDL_CloseAudioDevice(m_dAudioDev);
m_dAudioDev = 0;
}
}
// size需为PCM_BUFF_SIZE整数倍,除非为末尾数据
void AudioPcm::AudioThreadFunc()
{
char *read_data = NULL;
int max_play_data_len = 20000;
int ret;
int timeout_count = 0;
m_nCurAudioIndex = 0;
m_aAudioPlaying[m_nCurAudioIndex].bUsed = true;
m_aAudioPlaying[m_nCurAudioIndex].nPlayVolume = 50;
ret = AudioOpenDevice(0);
if (ret < 0)
{
m_aAudioPlaying[m_nCurAudioIndex].bUsed = false;
return;
}
read_data = (char *)malloc(max_play_data_len);
if (read_data == NULL)
{
cout << "malloc fifo data failed!\n";
m_aAudioPlaying[m_nCurAudioIndex].bUsed = false;
AudioCloseDevice();
return;
}
m_mutexAudioStream.lock();
m_aAudioPlaying[m_nCurAudioIndex].audioStream = SDL_NewAudioStream(m_nSampleFmt, m_nChannels, m_nSampleRate, m_nOutSampleFmt, m_nOutChannels, m_nOutSampleRate);
if (!m_aAudioPlaying[m_nCurAudioIndex].audioStream)
{
cout << "SDL_NewAudioStream error!\n";
}
m_mutexAudioStream.unlock();
while (m_bAudioThreadStart)
{
m_mutexAudioStream.lock();
ret = SDL_AudioStreamAvailable(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
if (ret <= 0)
{
//cout << "===========1==============wait data.........." << ret << "\n";
//调试发现刷新能减轻播放卡顿
SDL_AudioStreamFlush(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
ret = SDL_AudioStreamAvailable(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
}
if (ret <= 0)
{
//cout << "===========2==============wait data.........." << ret << "\n";
if (timeout_count >= 1000)
{
;// m_bAudioThreadStart = false;
}
m_mutexAudioStream.unlock();
usleep(10000);
timeout_count++;
continue;
}
else
{
timeout_count = 0;
cout << "=====ret data len.........." << ret << "\n";
if (ret >= max_play_data_len)
{
ret = SDL_AudioStreamGet(m_aAudioPlaying[m_nCurAudioIndex].audioStream, read_data, max_play_data_len);
SDL_AudioStreamClear(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
}
else
{
ret = SDL_AudioStreamGet(m_aAudioPlaying[m_nCurAudioIndex].audioStream, read_data, ret);
}
//SDL_QueueAudio(audio_dev, read_data, ret);
}
m_mutexAudioStream.unlock();
cout << "=====atual play data len.........." << ret << ", m_nSampleRate:" << m_nSampleRate << ", m_nOutSampleRate:" << m_nOutSampleRate << ", m_nNbSamples:" << m_nNbSamples << "\n";
m_aAudioPlaying[m_nCurAudioIndex].mutexAudioPlay.lock();
m_aAudioPlaying[m_nCurAudioIndex].pPlayPos = (uint8_t *)read_data;
m_aAudioPlaying[m_nCurAudioIndex].nPlayLen = ret; //长度为读出数据长度,在read_audio_data中做减法
m_aAudioPlaying[m_nCurAudioIndex].mutexAudioPlay.unlock();
while (1) //判断是否播放完毕
{
m_aAudioPlaying[m_nCurAudioIndex].mutexAudioPlay.lock();
if (m_aAudioPlaying[m_nCurAudioIndex].nPlayLen == 0)
{
m_aAudioPlaying[m_nCurAudioIndex].mutexAudioPlay.unlock();
timeout_count = 0;
SDL_AudioStreamFlush(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
break;
}
if (timeout_count >= 2000)
{
m_aAudioPlaying[m_nCurAudioIndex].mutexAudioPlay.unlock();
cout << "=========================wait data============================" << m_aAudioPlaying[m_nCurAudioIndex].nPlayLen << endl;
timeout_count = 0;
break;
}
m_aAudioPlaying[m_nCurAudioIndex].mutexAudioPlay.unlock();
timeout_count++;
SDL_Delay(1);
}
}
m_mutexAudioStream.lock();
if (m_aAudioPlaying[m_nCurAudioIndex].audioStream)
{
SDL_AudioStreamClear(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
SDL_FreeAudioStream(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
m_aAudioPlaying[m_nCurAudioIndex].audioStream = NULL;
}
SetAudioPlayUsed(m_nCurAudioIndex, false);
m_mutexAudioStream.unlock();
if (read_data)
{
free(read_data);
}
AudioCloseDevice();
}
int AudioPcm::AudioClearData()
{
m_mutexAudioStream.lock();
if (m_aAudioPlaying[m_nCurAudioIndex].audioStream)
{
SDL_AudioStreamClear(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
}
m_mutexAudioStream.unlock();
return 0;
}
int AudioPcm::AudioDataFinished()
{
int ret = -1;
m_mutexAudioStream.lock();
if (m_aAudioPlaying[m_nCurAudioIndex].audioStream)
{
ret = SDL_AudioStreamAvailable(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
}
m_mutexAudioStream.unlock();
return ret;
}
int AudioPcm::AudioPushData(char *data, int len)
{
int count = 0;
int rem = 2;
m_mutexAudioStream.lock();
//printf("=====%s(), %d, m_bAudioThreadStart:%d\n", __func__, __LINE__, m_bAudioThreadStart);
if (!m_bAudioThreadStart)
{
cout << "Audio push data, but thread stopped!\n";
m_mutexAudioStream.unlock();
return -2;
}
m_mutexAudioStream.unlock();
//printf("=====%s(), %d, data:%p, len:%d\n", __func__, __LINE__, data, len);
//G_INFO("=====" << __func__ << "(), line:" << __LINE__ << ", len:" << len << "\n");
switch (m_nSampleFmt)
{
case AUDIO_S16LSB:
case AUDIO_U16LSB:
case AUDIO_S16MSB:
case AUDIO_U16MSB:
rem=2;
break;
case AUDIO_U8:
case AUDIO_S8:
rem=1;
break;
default:
rem=4;
break;
}
if (len%(rem*m_nChannels) != 0)
{
len -= len%(rem*m_nChannels);
cout << "=====" << __func__ << "(), line:" << __LINE__ << ", len:" << len << "\n";
}
while (count < 500)
{
m_mutexAudioStream.lock();
if (m_aAudioPlaying[m_nCurAudioIndex].audioStream == NULL || SDL_AudioStreamPut(m_aAudioPlaying[m_nCurAudioIndex].audioStream, data, len) != 0)
{
m_mutexAudioStream.unlock();
usleep(2000);
count++;
continue;
}
//SDL_AudioStreamFlush(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
m_mutexAudioStream.unlock();
break;
}
if (count == 500)
{
cout << "Audio push data timeout!\n";
return -1;
}
return 0;
}
void AudioPcm::AudioThreadStart()
{
//size_t stackSize;
int policy = SCHED_FIFO;
sched_param param;
param.sched_priority = 3;
//printf("=====%s(), %d\n", __func__, __LINE__);
m_bAudioThreadStart = true;
m_threadAudio = SDL_CreateThread(StaticAudioThreadFunc, NULL, this);
//pthread_attr_init(&m_attrAudio);
//pthread_attr_setdetachstate(&m_attrAudio, PTHREAD_CREATE_DETACHED);
//pthread_attr_getstacksize(&m_attrAudio, &stackSize);
//pthread_attr_setstacksize(&m_attrAudio, stackSize*100);
//pthread_create(&m_threadAudio, NULL, &StaticAudioThreadFunc, this);
//pthread_setschedparam(m_threadAudio, policy, ¶m);
//m_threadAudio = std::thread(&AudioPcm::AudioThreadFunc, this);
//pthread_setschedparam(m_threadAudio.native_handle(), policy, ¶m);
}
void AudioPcm::AudioThreadStop()
{
int status = -1;
if (m_bAudioThreadStart)
{
m_bAudioThreadStart = false;
//加快线程退出速度
for (int i=0; i<AUDIO_PLAYING_MAX; i++)
{
std::unique_lock<std::mutex> locker(m_aAudioPlaying[i].mutexAudioPlay);
m_aAudioPlaying[i].nPlayLen = 0;
m_aAudioPlaying[i].pPlayPos = NULL;
}
//if (m_threadAudio.joinable()) {
// m_threadAudio.join();
//}
//pthread_join(m_threadAudio, NULL);
//pthread_attr_destroy(&m_attrAudio);
SDL_WaitThread(m_threadAudio, &status);
}
//printf("=====%s(), %d, m_bAudioThreadStart:%d\n", __func__, __LINE__, m_bAudioThreadStart);
}
bool AudioPcm::AudioThreadStatus()
{
return m_bAudioThreadStart;
}
int AudioPcm::AudioPlayStop(int audioIndex)
{
std::shared_ptr<AudioPcm> pAudioPlay = s_pAudioPlay;
if (pAudioPlay == nullptr)
{
cout << "No audio playing instanse!\n";
return -1;
}
if (audioIndex <= 0 || audioIndex >= AUDIO_PLAYING_MAX)
{
cout << __func__ << "Audio player index error!" << endl;
return -2;
}
if (!pAudioPlay->GetAudioPlayUsed(audioIndex))
{
cout << "AudioPlayer already stopped!\n";
return -3;
}
else
{
pAudioPlay->SetAudioPlayUsed(audioIndex, false);
}
return 0;
}
static int StaticAudioPlayDataThreadFunc(void *arg)
{
int audio_index = -1;
if (arg != NULL)
{
audio_index = *((char *)arg);
free(arg);
}
std::shared_ptr<AudioPcm> pAudioPlay = s_pAudioPlay;
SDL_AudioStream *audioStream;
char *read_data = NULL;
int ret;
if (audio_index <= 0 || audio_index >= AUDIO_PLAYING_MAX)
{
cout << __func__ << "Audio player index error! audio_index:" << audio_index << endl;
return -1;
}
cout << __func__ << "(), line:" << __LINE__ << ", audio_index:" << audio_index << endl;
audioStream = pAudioPlay->GetAudioPlayStream(audio_index);
if (audioStream == NULL)
{
pAudioPlay->SetAudioPlayUsed(audio_index, false);
return -2;
}
if (pAudioPlay == nullptr)
{
ret = -3;
goto __error2;
}
SDL_AudioStreamFlush(audioStream);
ret = SDL_AudioStreamAvailable(audioStream);
if (ret > 0)
{
cout << __func__ << "(), line:" << __LINE__ << ", audio_index:" << audio_index << endl;
read_data = (char *)malloc(ret);
if (read_data == NULL)
{
ret = -4;
goto __error2;
}
ret = SDL_AudioStreamGet(audioStream, read_data, ret);
pAudioPlay->SetAudioPlayData(audio_index, (uint8_t *)read_data, ret);
ret = pAudioPlay->AudioOpenDevice(audio_index);
if (ret != 0)
{
goto __error1;
}
}
cout << __func__ << "(), line:" << __LINE__ << ", audio_index:" << audio_index << endl;
while (pAudioPlay->GetAudioPlayUsed(audio_index) && pAudioPlay->GetAudioPlayLen(audio_index) > 0)
{
cout << "-----------------------------------\n";
SDL_Delay(1);
}
ret = 0;
pAudioPlay->AudioCloseDevice();
__error1:
if (read_data)
{
free(read_data);
}
__error2:
if (audioStream != NULL)
{
SDL_FreeAudioStream(audioStream);
audioStream = NULL;
}
pAudioPlay->SetAudioPlayUsed(audio_index, false);
return ret;
}
int AudioPcm::AudioPlayData(char *data, int len, int sampleRate, SDL_AudioFormat sampleFmt, int channels)
{
int ret;
std::shared_ptr<AudioPcm> pAudioPlay = s_pAudioPlay;
SDL_AudioStream *audioStream;
SDL_Thread *threadAudioPlay;
char *pAudioIndex = NULL;
int audio_index = -1;
if (data == NULL)
{
cout << "Audio player need data!\n";
return -1;
}
if (pAudioPlay == nullptr)
{
pAudioPlay = AudioPcm::CreateInstanse(sampleRate, sampleFmt, channels);
}
audio_index = pAudioPlay->RequestAudioPlay();
if (audio_index <= 0 || audio_index >= AUDIO_PLAYING_MAX)
{
cout << __func__ << ":Audio player request failed!" << endl;
return -1;
}
pAudioPlay->SetVolume(audio_index, SDL_MIX_MAXVOLUME);
audioStream = SDL_NewAudioStream(sampleFmt, channels, sampleRate, pAudioPlay->GetOutSampleFmt(), pAudioPlay->GetOutChannels(), pAudioPlay->GetOutSampleRate());
if (!audioStream)
{
cout << "SDL_NewAudioStream error!\n";
return -2;
}
pAudioPlay->SetAudioPlayStream(audio_index, audioStream);
if (SDL_AudioStreamPut(audioStream, data, len) != 0)
{
cout << "SDL_AudioStreamPut error!\n";
SDL_FreeAudioStream(audioStream);
audioStream = NULL;
return -3;
}
pAudioIndex = (char *)malloc(1);
if (pAudioIndex == NULL)
{
G_INFO("malloc error!\n");
SDL_FreeAudioStream(audioStream);
audioStream = NULL;
return -4;
}
*pAudioIndex = audio_index;
G_INFO(__func__ << "(), pAudioIndex:" << *pAudioIndex);
threadAudioPlay = SDL_CreateThread(StaticAudioPlayDataThreadFunc, "AudioPlay", pAudioIndex);
if (threadAudioPlay)
{
SDL_DetachThread(threadAudioPlay);
}
cout << "--------------audio_index:" << audio_index << endl;
return audio_index;
}
int AudioPcm::AudioPlayByFile(const char *filename, int sampleRate, SDL_AudioFormat sampleFmt, int channels)
{
int ret;
int file_size;
char *file_buffer;
int audio_index = -1;
if (filename == NULL)
{
cout << "Audio player need filename!\n";
}
FILE *fp = fopen(filename, "rb");
if (fp == NULL) {
cout << "cannot open this file\n";
return -1;
}
fseek(fp, 0, SEEK_END);
file_size = ftell(fp);
file_buffer = (char *)malloc(file_size);
if (file_buffer == NULL)
{
cout << "Malloc file size:" << file_size << " failed!\n";
fclose(fp);
return -2;
}
fseek(fp, 0, SEEK_SET);
ret = fread(file_buffer, 1, file_size, fp);
cout << "ret:" << ret <<", file_size:" << file_size << ", filename:" << filename << endl;
if (ret != file_size)
{
free(file_buffer);
fclose(fp);
return -1;
}
fclose(fp);
audio_index = AudioPlayData(file_buffer, file_size, sampleRate, sampleFmt, channels);
free(file_buffer);
return audio_index;
}
#endif
//测试代码
static void sigterm_handler(int sig)
{
AudioPcm::DeleteInstanse();
printf("=====%s(), %d\n", __func__, __LINE__);
exit(123);
}
void *PushThreadFunc(void *arg)
{
char *filename = (char *)arg;
int file_size;
char *file_buffer;
int count = 0;
int step = 1024;//必须8的倍数
int ret;
std::shared_ptr<AudioPcm> pSoundSpeaker;
FILE *fp = fopen(filename, "rb");
if (fp == NULL) {
printf("cannot open this file\n");
return NULL;
}
fseek(fp, 0, SEEK_END);
file_size = ftell(fp);
file_buffer = (char *)malloc(file_size);
fseek(fp, 0, SEEK_SET);
ret = fread(file_buffer, 1, file_size, fp);
printf("ret:%d, file_size:%d\n", ret, file_size);
if (ret != file_size)
{
free(file_buffer);
fclose(fp);
return NULL;
}
fclose(fp);
pSoundSpeaker = AudioPcm::GetInstanse();
if (pSoundSpeaker == nullptr)
{
return NULL;
}
while (count*step < file_size)
{
if ((count+1)*step > file_size) {
pSoundSpeaker->AudioPushData(file_buffer+count*step, file_size - count*step);
} else {
pSoundSpeaker->AudioPushData(file_buffer+count*step, step);
}
count++;
}
free(file_buffer);
return 0;
}
int main(int argc, char *argv[])
{
std::shared_ptr<AudioPcm> pSoundSpeaker;
pthread_t pushThread;
int ret;
signal(SIGINT , sigterm_handler); // Interrupt (ANSI).
signal(SIGTERM, sigterm_handler); // Termination (ANSI).
//pSoundSpeaker = AudioPcm::CreateInstanse(atoi(argv[3]), AUDIO_S16SYS, atoi(argv[4]));
//pthread_create(&pushThread, NULL, &PushThreadFunc, argv[1]);
//AudioPcm::AudioPlayByFile(argv[1], 48000, AUDIO_S16SYS, 1);
//AudioPcm::AudioPlayByFile(argv[2], 8000, AUDIO_S16SYS, 1);
while (1)
{
getchar();
AudioPcm::AudioPlayByFile("media/click.pcm", 48000, AUDIO_S16SYS, 1);
//sleep(2);
}
//pthread_join(pushThread, NULL);
//AudioPcm::DeleteInstanse();
return 0;
}
总结
在SDL2中,共提供了两套用于播放音频数据的API,本次介绍了使用回调函数播放音频数据的方式,且实现了多路音频播放。