最近在研究渲染問題,本文采用openal做pcm和wav資料流播放,并非本地檔案,demo是windows的,ios通用。網上都是ios的,ios需要引用OpenAl.framework架構,
Android平台需要做openal的jni,android的openal庫可以參考
下面是代碼:
//.h
/** Copyright (c/c++) <2016.11.22> <zwg/>
* Function
* OpenAL through the buffer queuing mechanism to support the streaming playback of sound. The buffer queue is a buffer associated with a single source contact mechanism.
* when audio playback, continuous rendering of each buffer, as if the buffer is composed of a continuous sound. This can be controlled by some special functions.
* flow is generally the source of the work. In a number of audio buffer by alSourceQueueBuffers () function to queue, and then play the sound source,
* next with property AL_BUFFERS_PROCESSED to query. This property obtains the number of buffers that have been processed,
* allows applications to use the alSourceUnqueueBuffers () function to delete the buffers that have been processed.
* alSourceUnqueueBuffers () function will start from the queue header will be processed in order to remove the buffer. Finally, the rest of the buffer queue in gear.
* Opanal for audio rendering related implementation and definition, etc.
* OpenAL通過緩沖器排隊機制支援聲音的流式播放。緩沖器排隊是多個緩沖器與單一音源相關聯的一種機制。
* 當音源播放時,連續對各個緩沖器進行渲染,就好象這些緩沖器組成了一個連續的聲音。這可以通過一些特殊函數來控制。
* 流音源的工作一般是這樣的。音源裡的一批緩沖器通過alSourceQueueBuffers()函數進行排隊,然後播放音源,
* 接下來用屬性AL_BUFFERS_PROCESSED來查詢。該屬性得出已經處理好的緩沖器的數量,
* 進而允許應用程式使用alSourceUnqueueBuffers()函數删除那些已經處理好的緩沖器。
* alSourceUnqueueBuffers()函數将從隊列頭部開始依次将處理好的緩沖器删除。最後,其餘的緩沖器在音源上排隊。
* OpanAl 用于音頻渲染相關實作及定義,等
*/
#ifndef __LVS_OPENAL_INTERFACE_H__
#define __LVS_OPENAL_INTERFACE_H__
#include <stdio.h>
#include <stdlib.h>
#include <string>
//windows
#ifdef WIN32
#include <Windows.h>
//openAl庫
#include "alut.h"
#pragma comment(lib,"alut.lib")
#pragma comment(lib,"OpenAL32.lib")
//ios
#elif __APPLE__
//ANDROID平台
#elif __ANDROID__
//linux
#else
#endif
//到處宏定義
#define LVS_DLLEXPORT __declspec(dllexport)
#define LVS_DLLEXPORT
using namespace std;
//接口初始化
int lvs_openal_interface_init();
//接口釋放
void lvs_openal_interface_uninit();
//接口開始播放
void lvs_openal_interface_playsound();
//接口停止播放
void lvs_openal_interface_stopsound();
//接口設定音量
void lvs_openal_interface_setvolume(float volume);//volume取值範圍(0~1)
//接口擷取音量
float lvs_openal_interface_getvolume();
//接口傳入pcm資料用于播放
int lvs_openal_interface_openaudiofromqueue(char* data,int dataSize,int aSampleRate,int aBit ,int aChannel);
//更新隊列資料,删除已經播放的buffer,這個在隊列滿的時候用
int lvs_openal_interface_updataQueueBuffer();
//擷取目前時間戳
long long lvs_openal_interface_getrealpts();
//擷取已經播放了多少個資料塊
long long lvs_openal_interface_getIsplayBufferSize();
//擷取緩存隊列長度
int lvs_openal_getnumqueuedsize();
class cclass_openal_interface;
class cclass_openal_interface
{
public:
cclass_openal_interface();
virtual ~cclass_openal_interface();
//開始播放
void playSound();
//停止播放
void stopSound();
//設定音量
void SetVolume(float volume);//volume取值範圍(0~1)
//擷取音量
float GetVolume();
//傳入pcm資料用于播放
int openAudioFromQueue(char* data,int dataSize,int aSampleRate,int aBit ,int aChannel);
//更新隊列資料,删除已經播放的buffer
int updataQueueBuffer();
private:
//初始化openal
int initOpenAL();
//釋放openal
void cleanUpOpenAL();
int m_numprocessed; //隊列中已經播放過的數量
int m_numqueued; //隊列中緩沖隊列數量
long long m_IsplayBufferSize; //已經播放了多少個音頻緩存數目
double m_oneframeduration; //一幀音頻資料持續時間(ms)
float m_volume; //目前音量volume取值範圍(0~1)
int m_samplerate; //采樣率
int m_bit; //樣本值
int m_channel; //聲道數
int m_datasize; //一幀音頻資料量
ALCdevice * m_Devicde; //device句柄
ALCcontext * m_Context; //device context
ALuint m_outSourceId; //source id 負責播放
};
//.cpp
#include "Lvs_OpenAl_Interface.h"
static cclass_openal_interface * copenal_interface = NULL;
int lvs_openal_interface_init()
int ret = 0;
printf("Device : lvs_openal_interface_init\n");
if(copenal_interface == NULL)
{
copenal_interface = new cclass_openal_interface();
}
return ret;
}
void lvs_openal_interface_uninit()
printf("Device : lvs_openal_interface_uninit\n");
if(copenal_interface)
delete copenal_interface;
copenal_interface = NULL;
return ;
void lvs_openal_interface_playsound()
copenal_interface->playSound();
void lvs_openal_interface_stopsound()
copenal_interface->stopSound();
void lvs_openal_interface_setvolume(float volume)//volume取值範圍(0~1)
copenal_interface->SetVolume(volume);
float lvs_openal_interface_getvolume()
return copenal_interface->GetVolume();
int lvs_openal_interface_openaudiofromqueue(char* data,int dataSize,int aSampleRate,int aBit ,int aChannel)
return copenal_interface->openAudioFromQueue(data,dataSize,aSampleRate,aBit,aChannel);
long long lvs_openal_interface_getrealpts()
long long time = (long long )((copenal_interface->m_IsplayBufferSize * copenal_interface->m_oneframeduration) + 0.5);
printf("*****m_IsplayBufferSize : %ld",copenal_interface->m_IsplayBufferSize);
printf("****************time : %lld(ms)\n",time);
return time;
long long lvs_openal_interface_getIsplayBufferSize()
return copenal_interface->m_IsplayBufferSize;
int lvs_openal_getnumqueuedsize()
return copenal_interface->m_numqueued;
int lvs_openal_interface_updataQueueBuffer()
return copenal_interface->updataQueueBuffer();
cclass_openal_interface::cclass_openal_interface()
m_Devicde = NULL;
m_Context = NULL;
m_outSourceId = 0;
m_numprocessed = 0;
m_numqueued = 0;
m_IsplayBufferSize = 0;
m_oneframeduration = 0.0;
m_volume = 1.0;
m_samplerate = 0;
m_bit = 0;
m_channel = 0;
m_datasize = 0;
//init
initOpenAL();
cclass_openal_interface::~cclass_openal_interface()
cleanUpOpenAL();
int cclass_openal_interface::initOpenAL()
printf("=======initOpenAl===\n");
//初始化 ALUT openal函數庫
int zwg_argc=1;
//添加函數庫名稱
char* zwg_argv[]={"ZWG_ALUT"};
ret= alutInit(&zwg_argc, zwg_argv);
//打開device
m_Devicde = alcOpenDevice(NULL);
if (m_Devicde)
//windows 用這個context 聲音不正常,以後處理
//建立聲音文本描述
m_Context = alcCreateContext(m_Devicde, NULL);
//設定行為文本描述
alcMakeContextCurrent(m_Context);
//建立一個source并設定一些屬性
alGenSources(1, &m_outSourceId);
alSpeedOfSound(1.0);
alDopplerVelocity(1.0);
alDopplerFactor(1.0);
alSourcef(m_outSourceId, AL_PITCH, 1.0f);
alSourcef(m_outSourceId, AL_GAIN, 1.0f);
alSourcei(m_outSourceId, AL_LOOPING, AL_FALSE);
alSourcef(m_outSourceId, AL_SOURCE_TYPE, AL_STREAMING);
void cclass_openal_interface::cleanUpOpenAL()
printf("=======cleanUpOpenAL===\n");
alDeleteSources(1, &m_outSourceId);
alcCloseDevice(m_Devicde);
m_Devicde = NULL;
alutExit();
ALCcontext * Context = alcGetCurrentContext();
ALCdevice * Devicde = alcGetContextsDevice(Context);
if (Context)
alcMakeContextCurrent(NULL);
alcDestroyContext(Context);
m_Context = NULL;
void cclass_openal_interface::playSound()
alSourcePlay(m_outSourceId);
if((ret = alGetError()) != AL_NO_ERROR)
printf("error alcMakeContextCurrent %x : %s\n", ret,alutGetErrorString (ret));
void cclass_openal_interface::stopSound()
alSourceStop(m_outSourceId);
void cclass_openal_interface::SetVolume(float volume)//volume取值範圍(0~1)
m_volume = volume;
alSourcef(m_outSourceId,AL_GAIN,volume);
float cclass_openal_interface::GetVolume()
return m_volume;
int cclass_openal_interface::updataQueueBuffer()
//播放狀态字段
ALint stateVaue = 0;
//擷取處理隊列,得出已經播放過的緩沖器的數量
alGetSourcei(m_outSourceId, AL_BUFFERS_PROCESSED, &m_numprocessed);
//擷取緩存隊列,緩存的隊列數量
alGetSourcei(m_outSourceId, AL_BUFFERS_QUEUED, &m_numqueued);
//擷取播放狀态,是不是正在播放
alGetSourcei(m_outSourceId, AL_SOURCE_STATE, &stateVaue);
//printf("===statevaue ========================%x\n",stateVaue);
if (stateVaue == AL_STOPPED ||
stateVaue == AL_PAUSED ||
stateVaue == AL_INITIAL)
//如果沒有資料,或資料播放完了
if (m_numqueued < m_numprocessed || m_numqueued == 0 ||(m_numqueued == 1 && m_numprocessed ==1))
{
//停止播放
printf("...Audio Stop\n");
stopSound();
cleanUpOpenAL();
return 0;
}
if (stateVaue != AL_PLAYING)
playSound();
//将已經播放過的的資料删除掉
while(m_numprocessed --)
ALuint buff;
//更新緩存buffer中的資料到source中
alSourceUnqueueBuffers(m_outSourceId, 1, &buff);
//删除緩存buff中的資料
alDeleteBuffers(1, &buff);
//得到已經播放的音頻隊列多少塊
m_IsplayBufferSize ++;
long long time = (long long )((m_IsplayBufferSize * m_oneframeduration) + 0.5);
//printf("*****m_IsplayBufferSize : %ld",m_IsplayBufferSize);
//printf("****************time : %ld(ms)\n",time);
return 1;
int cclass_openal_interface::openAudioFromQueue(char* data,int dataSize,int aSampleRate,int aBit ,int aChannel)
//樣本數openal的表示方法
ALenum format = 0;
//buffer id 負責緩存,要用局部變量每次資料都是新的位址
ALuint bufferID = 0;
if (m_datasize == 0 &&
m_samplerate == 0 &&
m_bit == 0 &&
m_channel == 0)
if (dataSize != 0 &&
aSampleRate != 0 &&
aBit != 0 &&
aChannel != 0)
m_datasize = dataSize;
m_samplerate = aSampleRate;
m_bit = aBit;
m_channel = aChannel;
m_oneframeduration = m_datasize * 1.0 /(m_bit/8) /m_channel /m_samplerate * 1000 ; //計算一幀資料持續時間
//建立一個buffer
alGenBuffers(1, &bufferID);
printf("error alGenBuffers %x : %s\n", ret,alutGetErrorString (ret));
//AL_ILLEGAL_ENUM
//AL_INVALID_VALUE
//#define AL_ILLEGAL_COMMAND 0xA004
//#define AL_INVALID_OPERATION 0xA004
if (aBit == 8)
if (aChannel == 1)
format = AL_FORMAT_MONO8;
else if(aChannel == 2)
format = AL_FORMAT_STEREO8;
if( aBit == 16 )
if( aChannel == 1 )
{
format = AL_FORMAT_MONO16;
if( aChannel == 2 )
format = AL_FORMAT_STEREO16;
//指定要将資料複制到緩沖區中的資料
alBufferData(bufferID, format, data, dataSize,aSampleRate);
printf("error alBufferData %x : %s\n", ret,alutGetErrorString (ret));
//附加一個或一組buffer到一個source上
alSourceQueueBuffers(m_outSourceId, 1, &bufferID);
printf("error alSourceQueueBuffers %x : %s\n", ret,alutGetErrorString (ret));
//更新隊列資料
ret = updataQueueBuffer();
//删除一個緩沖 這裡不應該删除緩沖,在source裡面播放完畢删除
//alDeleteBuffers(1, &bufferID);
bufferID = 0;
//main.cpp
//要顯示的pcm/wav檔案路徑及名稱
#define PCM_STREAM_PATH_NAME "../pcm_stream/44100_2_16.pcm"
int main()
int nSampleRate = 44100; //采樣率
int nBit = 16; //樣本數
int nChannel = 2; //聲道
int ndatasize = 1024 * (nBit/8) *nChannel; //每次讀取的資料大小
char ndata[4096 + 1] = {0}; //讀取的資料
FILE * pFile_pcm = NULL; //讀取pcm資料的檔案句柄
//打開pcm檔案
if((pFile_pcm = fopen(PCM_STREAM_PATH_NAME, "rb")) == NULL)
printf("filed open file : %s\n",PCM_STREAM_PATH_NAME);
return getchar();
else
printf("success open file : %s\n",PCM_STREAM_PATH_NAME);
lvs_openal_interface_init();
//設定音量volume取值範圍(0~1)
lvs_openal_interface_setvolume(1.0);
for(;;)
Sleep(23);
//循環讀取檔案
ret = fread(ndata, 1,ndatasize, pFile_pcm);
if (ret != ndatasize)
//seek到檔案開頭
fseek(pFile_pcm, 0, SEEK_SET);
fread(ndata, 1,ndatasize, pFile_pcm);
//具體的處理在這裡
ret = lvs_openal_interface_openaudiofromqueue((char *)ndata,ndatasize,nSampleRate,nBit,nChannel);
long long time = lvs_openal_interface_getrealpts();
//uinit
lvs_openal_interface_uninit();
//關閉pcm檔案
if (pFile_pcm != NULL)
fclose(pFile_pcm);
pFile_pcm = NULL;
程式運作效果并能聽到聲音:
本demo還需完善。
from:http://blog.csdn.net/zhuweigangzwg/article/details/53286945