前言
实时音频采集作为音频存储、处理的基础,无论在直播、通讯、音乐制作等领域中都扮演着重要的角色。通过选择合适的麦克风、采样率以及压缩算法等技术手段,可以达到高质量音频采集的目标。今天就详细的介绍一下各个平台的音频采集。
一、iOS端的音频采集有两种方式,一种是使用AVFoundation提供的AVAudioRecorder类进行录音,另一种是使用Core Audio框架中的Audio Unit实现音频采集。
下面详细介绍一下基于Core Audio框架的音频采集实现。
// 建立Audio Session
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
[session setMode:AVAudioSessionModeMeasurement error:nil];
[session setActive:YES error:nil];
// 创建Audio Unit对象
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc);
AudioUnit audioUnit;
AudioComponentInstanceNew(inputComponent, &audioUnit);
// 创建Audio Unit后需要对其进行配置,包括格式、采样率、音频输入和输出回调等参数的设置
UInt32 flag = 1;
AudioUnitSetProperty(
audioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
1,
&flag,
sizeof(flag)
);
AudioStreamBasicDescription audioFormat;
audioFormat.mSampleRate = 44100;
audioFormat.mFormatID = kAudioFormatLinearPCM;
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioFormat.mFramesPerPacket = 1;
audioFormat.mChannelsPerFrame = 2;
audioFormat.mBytesPerFrame = 4;
audioFormat.mBytesPerPacket = 4;
audioFormat.mBitsPerChannel = 16;
AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &audioFormat, sizeof(audioFormat));
AURenderCallbackStruct callback;
callback.inputProc = audioInputCallback;
callback.inputProcRefCon = (__bridge void *)(self); AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callback, sizeof(callback));
// 启动Audio
Unit AudioOutputUnitStart(audioUnit);
// audioInputCallback函数为数据回调函数,在每次有新的音频数据时会被调用。可以在这个函数中获取音频数据,进行后续处理或传输。
static OSStatus audioInputCallback(
void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
AudioBuffer buffer;
buffer.mNumberChannels = 2;
buffer.mDataByteSize = inNumberFrames * 4;
buffer.mData = malloc(inNumberFrames * 4);
AudioBufferList bufferList; bufferList.mNumberBuffers = 1;
bufferList.mBuffers[0] = buffer;
AudioUnitRender(audioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList); // 获取采集到的音频数据,进行后续处理或传输
free(bufferList.mBuffers[0].mData); return noErr;
})
总的来说,iOS端的音频采集可以基于AVFoundation提供的AVAudioRecorder类进行简单的录音操作,也可以使用Core Audio框架中的Audio Unit实现低延时、高质量的音频采集和处理,具体实现方式需要根据应用场景和需求进行选择。
二、Android平台上实现音频采集
大致可以分为以下几个步骤:
1. 获取音频设备 在Android的Audio系统中,音频设备是通过AudioManager来获取的。可以通过AudioManager的getDevices() 方法获取所有音频设备列表,或者通过getDevicesForStream()
2. 配置音频采集参数 根据需求配置音频采集参数,包括采样频率、采样位数、声道数等。可以通过AudioRecord类来实现配置。AudioRecord是Android平台上的录音类,它可以通过构造函数来传入采集参数,例如:
int audioSource = MediaRecorder.AudioSource.MIC;
int sampleRateInHz = 44100;
int channelConfig = AudioFormat.CHANNEL_IN_MONO;
int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
int bufferSizeInBytes = AudioRecord.getMinBufferSize(
sampleRateInHz,
channelConfig,
audioFormat
);
AudioRecord audioRecord = new AudioRecord(
audioSource,
sampleRateInHz,
channelConfig,
audioFormat,
bufferSizeInBytes
);
3. 采集音频数据 调用AudioRecord的 read()方法来采集音频数据。read()方法有多个重载,在本文中我们使用其中一个比较简单的重载,每次读取一定长度的数据,并将其存储到一个字节数组中。例如:
byte[] audioData = new byte[blockSizeInBytes];
audioRecord.read(audioData, 0, blockSizeInBytes);
需要注意的是,这里采集到的是PCM格式的音频数据,需要根据采集参数进行相应的解析。
4. 停止音频采集 在不需要采集音频数据时,需要调用AudioRecord的stop() 方法及release()
audioRecord.stop();
audioRecord.release();
以上就是Android平台上实现音频采集的简单介绍。需要注意的是,音频采集过程中需要申请权限,并且考虑到设备的不同,配置参数和实现方式会有所不同。
三、Windows端音频采集
在Windows平台上进行音频采集需要使用Windows Multimedia API (MME)或者Windows Core Audio API。这里我们以Windows Core Audio API为例,介绍一下Windows端音频采集的代码实现。
1. 初始化设备 初始化设备需要使用MMDevice API,首先需要创建一个IMMDeviceEnumerator接口,并使用GetDefaultAudioEndpoint()方法获取默认音频输入设备。
IMMDeviceEnumerator* deviceEnumerator = nullptr;
IMMDevice* audioDevice = nullptr;
HRESULT hr = CoCreateInstance( __uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), reinterpret_cast<void**>(&deviceEnumerator) );
if (FAILED(hr)) {
fprintf(stderr, "ERROR: CoCreateInstance failed.\n");
return EXIT_FAILURE;
}
hr = deviceEnumerator->GetDefaultAudioEndpoint(eCapture, eCommunications, &audioDevice);
if (FAILED(hr)) {
fprintf(stderr, "ERROR: GetDefaultAudioEndpoint failed.\n");
deviceEnumerator->Release();
return EXIT_FAILURE;
}
deviceEnumerator->Release();
2. 获取音频格式 接着需要获取音频输入设备当前支持的音频格式。我们可以使用 IAudioClient
IAudioClient* audioClient = nullptr;
IAudioCaptureClient* captureClient = nullptr;
WAVEFORMATEX* pwfx = nullptr;
hr = audioDevice->Activate( __uuidof(IAudioClient), CLSCTX_ALL, nullptr, reinterpret_cast<void**>(&audioClient) );
if (FAILED(hr)) {
fprintf(stderr, "ERROR: Activate failed.\n");
audioDevice->Release();
return EXIT_FAILURE;
}
// Get the default device format.
hr = audioClient->GetMixFormat(&pwfx);
if (FAILED(hr)) {
fprintf(stderr, "ERROR: GetMixFormat failed.\n");
audioClient->Release();
audioDevice->Release();
return EXIT_FAILURE;
}
// Ensure the format support.
hr = audioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, REFTIMES_PER_SEC, 0, pwfx, nullptr );
if (FAILED(hr)) {
fprintf(stderr, "ERROR: Initialize failed.\n");
audioClient->Release();
audioDevice->Release();
CoTaskMemFree(pwfx);
return EXIT_FAILURE;
}
UINT32 bufferFrameCount = 0;
hr = audioClient->GetBufferSize(&bufferFrameCount);
if (FAILED(hr)) {
fprintf(stderr, "ERROR: GetBufferSize failed.\n");
audioClient->Release();
audioDevice->Release();
CoTaskMemFree(pwfx);
return EXIT_FAILURE;
}
3. 创建采样线程 在初始化完设备后,我们需要创建一个子线程并在其中不断地循环读取数据。这里我们使用IAudioCaptureClient接口的GetBuffer() 方法来获取音频数据,并使用ReleaseBuffer()
DWORD taskThreadId;
HANDLE taskThreadHandle = CreateThread( nullptr, 0, AudioCaptureThread, reinterpret_cast<void*>(audioClient), 0, &taskThreadId );
if (taskThreadHandle == nullptr) {
fprintf(stderr, "ERROR: CreateThread failed.\n");
audioClient->Release();
audioDevice->Release();
CoTaskMemFree(pwfx);
return EXIT_FAILURE;
}
在子线程中,我们循环读取采样缓冲区中的音频数据:
DWORD WINAPI AudioCaptureThread(LPVOID lpParameter) {
IAudioClient* audioClient = reinterpret_cast<IAudioClient*>(lpParameter);
IAudioCaptureClient* captureClient = nullptr;
WAVEFORMATEX* pwfx = nullptr;
UINT32 bufferFrameCount = 0;
DWORD flags = 0;
BYTE* pData = nullptr;
DWORD n = 0;
// Get the capture client. HRESULT
hr = audioClient->GetService( __uuidof(IAudioCaptureClient), reinterpret_cast<void**>(&captureClient) );
if (FAILED(hr)) {
fprintf(stderr, "ERROR: GetService failed.\n");
return EXIT_FAILURE;
}
// Get the buffer size.
hr = audioClient->GetBufferSize(&bufferFrameCount);
if (FAILED(hr)) {
fprintf(stderr, "ERROR: GetBufferSize failed.\n");
captureClient->Release();
return EXIT_FAILURE;
}
// Get the default device format.
hr = audioClient->GetMixFormat(&pwfx);
if (FAILED(hr)) {
fprintf(stderr, "ERROR: GetMixFormat failed.\n");
captureClient->Release();
return EXIT_FAILURE;
}
// Start capturing.
hr = audioClient->Start();
if (FAILED(hr)) {
fprintf(stderr, "ERROR: Start failed.\n");
captureClient->Release();
return EXIT_FAILURE;
}
// Capture loop.
while (g_audioCapturing) {
// Get the next packet.
hr = captureClient->GetBuffer(&pData, &n, &flags, nullptr, nullptr);
if (FAILED(hr)) {
fprintf(stderr, "ERROR: GetBuffer failed.\n");
captureClient->Release();
return EXIT_FAILURE;
}
if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
memset(pData, 0, n);
}
// Do something with the audio data.
// ...
// Release the packet.
hr = captureClient->ReleaseBuffer(n);
if (FAILED(hr)) {
fprintf(stderr, "ERROR: ReleaseBuffer failed.\n");
captureClient->Release();
return EXIT_FAILURE;
}
} // Stop capturing.
hr = audioClient->Stop();
if (FAILED(hr)) {
fprintf(stderr, "ERROR: Stop failed.\n");
captureClient->Release();
return EXIT_FAILURE;
}
captureClient->Release();
return EXIT_SUCCESS;
}
4. 停止采集 最后,在程序退出时需要调用IAudioClient接口的 Stop()
// Stop capturing.
g_audioCapturing = false;
WaitForSingleObject(taskThreadHandle, INFINITE);
HRESULT hr = audioClient->Stop();
if (FAILED(hr)) {
fprintf(stderr, "ERROR: Stop failed.\n");
}
audioClient->Release();
audioDevice->Release();
CoTaskMemFree(pwfx);
实现window端的音频采集,除了 Win32 API 之外,还可以使用第三方库进行音频采集。目前比较流行的音频采集库包括 OpenAL、ASIO、PortAudio 等。
至此,我们分别在iOS、Android、Windows平台实现是了音频采集。