前面的文章讲了如何使用OpenAL对音频添加音效,并播放,参见:
OpenAL 使用基本流程
使用OpenAL混音,添加音频特效
本文谈谈如何对音频进行渲染,然后存储下来。
初始化过程与之前的文章(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;
}
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());
代码如下:
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 使用基本流程
void CHXALRender::getRenderedData(void *data, int len)
{
alcRenderSamplesSOFT(m_device, data, len/mFrameSize);
}