zoukankan      html  css  js  c++  java
  • iOS OpenAL播放声音的流程

    iOS移动设备进行音频播放时,可以使用openal

    注意:openal 默认开启的是手机听筒(有多个播放设备 :alcopendevice(null)),如果想让声音通过扬声器进行播放,可通过audiosession的audiosessionsetproperty进行设置:kaudiosessionproperty_overridecategorydefaulttospeaker,此时声音会即从扬声器出来,也从听筒出来。

    使用openal播放声音的步骤:

    1 alcopendevice(null)-----得到设备D

    获取设备可以通过打开一个null直接打开听筒,也可以枚举后,选择一个使用。如下:

    复制代码
    - (void) enumDevices {
        ALboolean enumeration;
     
        enumeration = alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT");
        if (enumeration == AL_FALSE) {
            NSLog(@"iOS dosn't support ALC_ENUMERATION_EXT");
            return;
        }
     
        ALCchar * devices = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
        //ALCchar * devices = alcGetString(NULL, ALC_DEFAULT_ALL_DEVICES_SPECIFIER);
        const ALCchar *device = devices, *next = devices + 1;
        size_t len = 0;
     
        NSLog(@"Devices list:
    ");
        while (device && *device != '' ) {
            NSLog(@"    -> %s", device);
            len = strlen(device);
            device += (len + 1);
            next += (len + 2);
        }
    
    ————————————————
    版权声明:本段代码转载自「老衲不出家」
    复制代码

    2 alccreateContext(D,hull)-----创建上下文context

    3 alcMakeContextCurrent(context)----将上一步创建的上下文设置为当前上下文,注意“当前”两个字,如果创建两次上下文(并且两个context不是同一个,譬如地址不同,这算两个不一样的上下文),并且这两个都设置成当前上下文(makecontextcurrent),那第一次创建的上下文就会被最新的覆盖掉,第一次上下文下边的声源,也就不再播放声音了。只有最新的 currentcontext才起作用。设备,声源,收听者,上下文的关系,可参见openal官方文档。

    举例:在Windows当中,功能1创建一个上下文1,用一个播放设备绑定上下文1,然后播放音乐A。然后开启功能2,也重新创建了上下文2,并绑定播放设备(功能2 可以和功能1用同一个设备,也可以是不同的设备,声卡),功能2播放音乐B,只要功能2重新设置了当前上下文,功能2播放声音B时,功能1的声音A就不在播放了。(window 是如此的),要想功能1和功能2都有声音,可以在开启功能2时候获取当前上下文(getcurrentcontext),如果有,就不要再创建了,直接获取到当前上下文,并把当前上下文再次设置一次makecurrentcontext. 设备一般都是用的同一个,只要再打开一下就行。

    4 申请播放缓存algenbuffers(个数,buffer个数),algenbuffers(6,p_buffer),定义  int  p_buffer[6];也可以是1个,前边表示开了6块缓存;int型主要是用于存放地地址。

    5 设置一个声源 alGenSources(1, &uisources); 给生源设置一些属性 alSourcei/f函数; 音源与buffer绑定alSourcei(uisources, AL_BUFFER, p_buffer);(绑定操作没有也可以);属性设置 alSourcei(uisources, AL_LOOPING, AL_FALSE); //设置多个缓存时,多个缓存播放 ,不能循环

    6 alBufferData(p_buffer[i], AL_FORMAT_MONO16, data, size, 采样率);将数据装载到缓存上去,只有一个就装一个,多个就循环装载

       alSourceQueueBuffers(uisources, 1, &p_buffer[n]),缓冲加到数据源上去。

    7 播放声源 alSourcePlay(sources);

    8 当使用多个缓冲播放时,通过不断获取声源空闲buffer个数,来不断地填充,algetsourcei(uisource,AL_BUFFERS_PROCESSED,&Buffernum);获取到了的话(即 Buffernum>0)->从队列中移除播放结束的buffer,并取得buffer的标志(或者说位置)进行数据填充,操作如下:如果Buffernum>0,说明有空闲或者已播放完的位置,则调用 alSourceUnqueueBuffers(uiSource, 1, &BufferPOS);【BufferPOS为获取到的空闲buffer的地址】,然后在刚获取的这块空闲buffer的地址(BufferPOS)上,放新的数据,alBufferData(BufferPOS, ulFormat, pData, size, samplerate); alSourceQueueBuffers(uiSource, 1, &BufferPOS); 如果长时间获取不到空闲缓存 或者说获取不到已经处理完的个数,那就不能老是卡在这获取,可以设置一定的时间,获取不到空闲缓存,这次播放就算了,或者让上层去决定这次的数据要不要播放,以缓解没有空闲播放缓存的尴尬。

    这里可以借鉴:https://blog.csdn.net/little_tan/article/details/17028517

    复制代码
    该代码:来自 冬南风的博客
    DWORD WINAPI COpenAl::PlayThread(LPVOID lpParame) { COpenAl *cOpenAl = (COpenAl*)lpParame; unsigned int nBytesWritten = 0; while(true) { //openal buffers中已经播放过的个数 cOpenAl->m_iBuffersProcessed = 0; //得到空闲的buffer个数 alGetSourcei(cOpenAl->m_uiSource, AL_BUFFERS_PROCESSED, &cOpenAl->m_iBuffersProcessed); //对于每一个空闲的buffer,填充新的数据并添加到播放队列中 while(cOpenAl->m_iBuffersProcessed) { //从队列中移除播放结束的buffer,并取得buffer的标志进行数据填充 cOpenAl->m_uiBuffer = 0; alSourceUnqueueBuffers(cOpenAl->m_uiSource, 1, &cOpenAl->m_uiBuffer); //读取数据,并检查文件是否播放完成 if(cOpenAl->m_pWavInfo != NULL && cOpenAl->m_pWavInfo->ReadABlockData(cOpenAl->m_pData, &nBytesWritten) == 0) { //拷贝数据到buffer中 alBufferData(cOpenAl->m_uiBuffer, cOpenAl->m_ulFormat, cOpenAl->m_pData, nBytesWritten, cOpenAl->m_ulFrequency); //将填充好的buffer添加到播放队列中 alSourceQueueBuffers(cOpenAl->m_uiSource, 1, &cOpenAl->m_uiBuffer); } cOpenAl->m_iBuffersProcessed--; } // Check the status of the Source. If it is not playing, then playback was completed, // or the Source was starved of audio data, and needs to be restarted. alGetSourcei(cOpenAl->m_uiSource, AL_SOURCE_STATE, &cOpenAl->m_iState); if(cOpenAl->m_iState != AL_PLAYING) { // If there are Buffers in the Source Queue then the Source was starved of audio // data, so needs to be restarted (because there is more audio data to play) alGetSourcei(cOpenAl->m_uiSource, AL_BUFFERS_QUEUED, &cOpenAl->m_iQueuedBuffers); if(cOpenAl->m_iQueuedBuffers) { alSourcePlay(cOpenAl->m_uiSource); } else { // Finished playing break; } } } return 0; }

    首次 先填充数据并启动,后边的(非第一次)就可以循环检测并送数据到声源操作。
    复制代码

    播放声道数:albufferData

    采集声道数:alcOpenCapDevice

  • 相关阅读:
    Django restframework api版本控制组件增加及源码分析
    Django restframework用户访问频率控制组件增加及源码分析
    Django restframework用户权限认证组件增加及源码分析
    Django restframework用户登录认证组件增加及源码分析
    Django restframework视图访问流程源码剖析
    01 二维数组中的查找
    Linux基础
    函数设计原则
    递归函数分析
    函数与宏分析
  • 原文地址:https://www.cnblogs.com/8335IT/p/12305662.html
Copyright © 2011-2022 走看看