1. 程式人生 > >通過OpenAL對音訊新增音效並存儲

通過OpenAL對音訊新增音效並存儲

1. 前言

本文談談如何對音訊進行渲染,然後儲存下來。

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