天天看點

OpenAL播放pcm或wav資料流-windows/ios/android(一)

最近在研究渲染問題,本文采用openal做pcm和wav資料流播放,并非本地檔案,demo是windows的,ios通用。網上都是ios的,ios需要引用OpenAl.framework架構,

Android平台需要做openal的jni,android的openal庫可以參考

下面是代碼:

//.h

OpenAL播放pcm或wav資料流-windows/ios/android(一)

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

OpenAL播放pcm或wav資料流-windows/ios/android(一)

#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

OpenAL播放pcm或wav資料流-windows/ios/android(一)

//要顯示的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;  

程式運作效果并能聽到聲音:

OpenAL播放pcm或wav資料流-windows/ios/android(一)

本demo還需完善。

from:http://blog.csdn.net/zhuweigangzwg/article/details/53286945

下一篇: XAudio2播放PCM

繼續閱讀