您的位置:首页 > 移动开发 > Android开发

OpenAL播放pcm或wav数据流-windows/ios/android(一)

2017-11-01 12:01 736 查看
OpenAL播放pcm或wav数据流-windows/ios/android(一)

最近在研究渲染问题,本文采用openal做pcm和wav数据流播放,并非本地文件,demo是windows的,ios通用。网上都是ios的,ios需要引用OpenAl.framework框架,

Android平台需要做openal的jni,android的openal库可以参考
http://blog.csdn.net/matrix_laboratory/article/details/53319735这篇文章,各个平台需要做稍微处理。
下面是代码:

//.h

[cpp] view
plain copy

/** 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__

#include "alut.h"

//ANDROID平台

#elif __ANDROID__

#include "alut.h"

//linux

#else

#include "alut.h"

#endif

//到处宏定义

//windows

#ifdef WIN32

#define LVS_DLLEXPORT __declspec(dllexport)

//ios

#elif __APPLE__

#define LVS_DLLEXPORT

//linux

#else

#define LVS_DLLEXPORT

#endif

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();

public:

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; //一帧音频数据量

private:

ALCdevice * m_Devicde; //device句柄

ALCcontext * m_Context; //device context

ALuint m_outSourceId; //source id 负责播放

};

#endif

//.cpp

[cpp] view
plain copy

#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();

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;

}

int cclass_openal_interface::initOpenAL()

{

int ret = 0;

printf("=======initOpenAl===\n");

#ifdef WIN32

//初始化 ALUT openal函数库

int zwg_argc=1;

//添加函数库名称

char* zwg_argv[]={"ZWG_ALUT"};

ret= alutInit(&zwg_argc, zwg_argv);

#else

#endif

//打开device

m_Devicde = alcOpenDevice(NULL);

if (m_Devicde)

{

#ifdef WIN32

//windows 用这个context 声音不正常,以后处理

#else

//建立声音文本描述

m_Context = alcCreateContext(m_Devicde, NULL);

//设置行为文本描述

alcMakeContextCurrent(m_Context);

#endif

}

//创建一个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);

return ret;

}

void cclass_openal_interface::cleanUpOpenAL()

{

printf("=======cleanUpOpenAL===\n");

alDeleteSources(1, &m_outSourceId);

#ifdef WIN32

alcCloseDevice(m_Devicde);

m_Devicde = NULL;

alutExit();

#else

ALCcontext * Context = alcGetCurrentContext();

ALCdevice * Devicde = alcGetContextsDevice(Context);

if (Context)

{

alcMakeContextCurrent(NULL);

alcDestroyContext(Context);

m_Context = NULL;

}

alcCloseDevice(m_Devicde);

m_Devicde = NULL;

#endif

}

void cclass_openal_interface::playSound()

{

int ret = 0;

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)

{

int ret = 0;

//样本数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);

if((ret = alGetError()) != AL_NO_ERROR)

{

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);

if((ret = alGetError()) != AL_NO_ERROR)

{

printf("error alBufferData %x : %s\n", ret,alutGetErrorString (ret));

//AL_ILLEGAL_ENUM

//AL_INVALID_VALUE

//#define AL_ILLEGAL_COMMAND 0xA004

//#define AL_INVALID_OPERATION 0xA004

}

//附加一个或一组buffer到一个source上

alSourceQueueBuffers(m_outSourceId, 1, &bufferID);

if((ret = alGetError()) != AL_NO_ERROR)

{

printf("error alSourceQueueBuffers %x : %s\n", ret,alutGetErrorString (ret));

}

//更新队列数据

ret = updataQueueBuffer();

//删除一个缓冲 这里不应该删除缓冲,在source里面播放完毕删除

//alDeleteBuffers(1, &bufferID);

bufferID = 0;

return 1;

}

//main.cpp

[cpp] view
plain copy

#include "Lvs_OpenAl_Interface.h"

//要显示的pcm/wav文件路径及名称

#define PCM_STREAM_PATH_NAME "../pcm_stream/44100_2_16.pcm"

int main()

{

int ret = 0;

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);

}

//init

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;

}

return 1;

}

程序运行效果并能听到声音:



本demo还需完善。

如有错误请指正:


交流请加QQ群:62054820

QQ:379969650.

原文转载自:http://blog.csdn.net/zhuweigangzwg/article/details/53286945
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: