通过OpenAL对音频添加音效并存储

1. 前言

前面的文章讲了如何使用OpenAL对音频添加音效,并播放,参见:
OpenAL 使用基本流程
使用OpenAL混音,添加音频特效

本文谈谈如何对音频进行渲染,然后存储下来。


2. 初始化

初始化过程与之前的文章(OpenAL 使用基本流程)提到了基本一致,下面做了略微修改:

    bool CHXALRender::init(int channels, int sampleRate)
    {
        if (m_context != nullptr)
        {
            clear();
        }

        ALCint attrs[8];
        attrs[0] = ALC_FORMAT_CHANNELS_SOFT;

        if(channels == 1)
            attrs[1] = ALC_MONO_SOFT;
        else if(channels == 2)
            attrs[1] = ALC_STEREO_SOFT;
        else
        {
            XLOGE("Unhandled SDL channel count: %d\n", channels);
            return false;
        }

        attrs[2] = ALC_FORMAT_TYPE_SOFT;
        attrs[3] = ALC_SHORT_SOFT;

        attrs[4] = ALC_FREQUENCY;
        attrs[5] = sampleRate;

        attrs[6] = 0;

        mFrameSize = FramesToBytes(1, attrs[1], attrs[3]);

        m_device = alcLoopbackOpenDeviceSOFT(NULL);
        if(m_device == nullptr)
        {
            XLOGI("Create device failed!\n");
            return false;
        }

        if(alcIsRenderFormatSupportedSOFT(m_device, attrs[5], attrs[1], attrs[3]) == ALC_FALSE)
        {
            XLOGE("Render format not supported: %d, %d, %dhz\n", attrs[1], attrs[3], attrs[5]);
            return false;
        }

        m_context = alcCreateContext(m_device, attrs);
        if(m_context == nullptr)
        {
            XLOGE("Create context failed!\n");;
            return false;
        }

        alcMakeContextCurrent(m_context);
        alGenBuffers(MAX_CACHE, m_buffer);
        alGenSources(1, &m_source);

        if(CheckALError("CHXALPlayBack::init"))
            return false;

        for(auto buf : m_buffer)
        {
            m_bufferQueue.push_back(buf);
        }

        return true;
    }

3. 处理流程

mIsRendering = true;
        do {
            size_t size = mWaveReader->readData(buffer, kByte, kChunckSize/kByte);
            if (size <= 0)
            {
                XLOGI("Read end.");
                break;
            }
            else
            {
                readDataSize += size;
                XLOGI("Read data %d.", readDataSize);
            }
            mALRender->render(buffer, kByte * size, format, header->samplesPerSec);

            getRenderedData(buffer, kByte * size);
            size = fwrite(buffer, kByte, size, fout);
        }while(1);

        do {
            getRenderedData(buffer, kByte * size);
            size = fwrite(buffer, kByte, size, fout);
        } while(alGetError() == AL_NO_ERROR && mALRender->isPlaying());

4. 渲染render

代码如下:

    bool CHXALRender::render(const void *data, int dataSize, int format, int freq)
    {
        recycle();
        if (data == nullptr)
        {
            return false;
        }

        ALuint buffer = 0;

        if (m_bufferQueue.empty())
        {
            alGenBuffers(1, &buffer);
        }
        else
        {
            buffer = m_bufferQueue.front();
            m_bufferQueue.pop_front();
        }

        alBufferData(buffer, format, data, dataSize, freq);
        alSourceQueueBuffers(m_source, 1, &buffer);

        resume();
    }

recycle: 从OpenAL中回收buffer
resume: PlaySource
参见OpenAL 使用基本流程


5. 获取渲染后的数据getRenderedData

void CHXALRender::getRenderedData(void *data, int len)
{
    alcRenderSamplesSOFT(m_device, data, len/mFrameSize);
}

你可能感兴趣的:(多媒体,OpenAL,Android,FFmpeg多媒体)