ios 核心音频:如何从带有交错音频的 AudioBuffer 中获取样本
ios Core audio: how to get samples from AudioBuffer with interleaved audio
我已使用 ExtAudioFileRead
函数将音频文件读入 AudioBufferList
。
这是音频的 ASBD:
AudioStreamBasicDescription importFormat;
importFormat.mFormatID = kAudioFormatLinearPCM;
importFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
importFormat.mBytesPerPacket = 4;
importFormat.mFramesPerPacket = 1;
importFormat.mBytesPerFrame = 4;
importFormat.mChannelsPerFrame = 2;
importFormat.mBitsPerChannel = 16;
importFormat.mSampleRate = [[AVAudioSession sharedInstance] sampleRate];
所以我们得到并交错了 2 个通道的音频,每个通道有 16 位符号整数
AudioBufferList
初始化:
UInt32 *audioData = (UInt32 *) calloc (totalFramesInFile, sizeof (UInt32));
AudioBufferList *bufferList;
bufferList = (AudioBufferList *) malloc (sizeof (AudioBufferList));
// buffers amount is 1 because audio is interleaved
bufferList->mNumberBuffers = 1;
bufferList->mBuffers[0].mNumberChannels = 2;
bufferList->mBuffers[0].mDataByteSize = totalFramesInFile * sizeof(UInt32);
bufferList->mBuffers[0].mData = audioData;
并读入缓冲区:
CheckError(ExtAudioFileRead (
audioFileObject,
&numberOfPacketsToRead,
bufferList), "error ExtAudioFileRead");
audioFileObject
是 ExtAudioFileRef
的实例,它是在代码中启动的,我没有粘贴在这里以保存 space。
我想要完成的是在我的渲染回调中修改音频样本。
OSStatus MyCallback (void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData){
ViewController *view = (__bridge ViewController *) inRefCon;
soundStruct *soundStruct = (soundStruct *) &view->mys;
SInt64 frameTotalForSound = soundStruct->frameCount;
soundStruct->isPlaying = true;
UInt32 *audioData = soundStruct->audioData;
UInt32 sampleNumber = soundStruct->sampleNumber;
for( int i = 0; i < ioData->mNumberBuffers; i++){
AudioBuffer buffer = ioData->mBuffers[i];
UInt32 *frameBuffer = buffer.mData;
for(UInt32 frame = 0; frame < inNumberFrames; frame++) {
// here I fill the buffer with my audio data.
// i need to get left and right channel samples
// from audioData[sampleNumber], modify them
// and write into frameBuffer
frameBuffer[frame] = audioData[sampleNumber];
sampleNumber++;
if(sampleNumber > frameTotalForSound) {
soundStruct->isPlaying = false;
AudioOutputUnitStop(soundStruct->outputUnit);
}
}
}
soundStruct->sampleNumber = sampleNumber;
return noErr;
}
是否可以从音频数据的UInt32数组中获取Sint16左右声道样本?
让audioData
和frameBuffer
都为SInt16
s:
SInt16 *audioData;
// ...
SInt16 *frameBuffer;
您的缓冲区大小计算应 n * 2 * sizeof(SInt16) and you'll either need to change
soundStruct` 或添加类型转换。
然后您可以像这样访问交错样本:
frameBuffer[0] = modify(audioData[0]); // left sample 1
frameBuffer[1] = modify(audioData[1]); // right sample 1
frameBuffer[2] = modify(audioData[2]); // left sample 2
frameBuffer[3] = modify(audioData[3]); // right sample 2
// ...
frameBuffer[2*(n-1)] = modify(audioData[2*(n-1)]); // left sample n
frameBuffer[2*(n-1)+1] = modify(audioData[2*(n-1)+1]); // right sample n
@Rhythmic Fistman,非常感谢 - 它很有帮助。
我无法设置 frameBuffer
以这种方式工作。声音在输出端失真。
我猜这是因为 AudioUnit 期望在一帧中包含两个通道数据。或者也许还有其他解释。
这是我修改的代码,希望对大家有所帮助:
audioData
初始化:
SInt16 *audioData = (SInt16 *) malloc (sizeof(SInt16) * totalFramesInFile * 2);
修改渲染回调:
OSStatus MyCallback (void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
ViewController *view = (__bridge ViewController *) inRefCon;
soundStruct *soundStruct = (soundStruct *) &view->mys;
SInt64 frameTotalForSound = soundStruct->frameCount;
soundStruct->isPlaying = true;
SInt16 *audioData = soundStruct->audioData;
UInt32 sampleNumber = soundStruct->sampleNumber;
for( int i = 0; i < ioData->mNumberBuffers; i++){
AudioBuffer buffer = ioData->mBuffers[i];
SInt16 *frameBuffer = (SInt16*) ioData->mBuffers[0].mData;
for(UInt32 frame = 0; frame < inNumberFrames * 2; frame+=2) {
/* .. some samples modification code .. */
// left channel
frameBuffer[frame] = audioData[sampleNumber];
// right channel
frameBuffer[frame + 1] = audioData[sampleNumber + 1];
sampleNumber +=2;
if(sampleNumber > frameTotalForSound * 2) {
soundStruct->isPlaying = false;
AudioOutputUnitStop(soundStruct->outputUnit);
}
}
}
soundStruct->sampleNumber = sampleNumber;
return noErr;
}
我已使用 ExtAudioFileRead
函数将音频文件读入 AudioBufferList
。
这是音频的 ASBD:
AudioStreamBasicDescription importFormat;
importFormat.mFormatID = kAudioFormatLinearPCM;
importFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
importFormat.mBytesPerPacket = 4;
importFormat.mFramesPerPacket = 1;
importFormat.mBytesPerFrame = 4;
importFormat.mChannelsPerFrame = 2;
importFormat.mBitsPerChannel = 16;
importFormat.mSampleRate = [[AVAudioSession sharedInstance] sampleRate];
所以我们得到并交错了 2 个通道的音频,每个通道有 16 位符号整数
AudioBufferList
初始化:
UInt32 *audioData = (UInt32 *) calloc (totalFramesInFile, sizeof (UInt32));
AudioBufferList *bufferList;
bufferList = (AudioBufferList *) malloc (sizeof (AudioBufferList));
// buffers amount is 1 because audio is interleaved
bufferList->mNumberBuffers = 1;
bufferList->mBuffers[0].mNumberChannels = 2;
bufferList->mBuffers[0].mDataByteSize = totalFramesInFile * sizeof(UInt32);
bufferList->mBuffers[0].mData = audioData;
并读入缓冲区:
CheckError(ExtAudioFileRead (
audioFileObject,
&numberOfPacketsToRead,
bufferList), "error ExtAudioFileRead");
audioFileObject
是 ExtAudioFileRef
的实例,它是在代码中启动的,我没有粘贴在这里以保存 space。
我想要完成的是在我的渲染回调中修改音频样本。
OSStatus MyCallback (void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData){
ViewController *view = (__bridge ViewController *) inRefCon;
soundStruct *soundStruct = (soundStruct *) &view->mys;
SInt64 frameTotalForSound = soundStruct->frameCount;
soundStruct->isPlaying = true;
UInt32 *audioData = soundStruct->audioData;
UInt32 sampleNumber = soundStruct->sampleNumber;
for( int i = 0; i < ioData->mNumberBuffers; i++){
AudioBuffer buffer = ioData->mBuffers[i];
UInt32 *frameBuffer = buffer.mData;
for(UInt32 frame = 0; frame < inNumberFrames; frame++) {
// here I fill the buffer with my audio data.
// i need to get left and right channel samples
// from audioData[sampleNumber], modify them
// and write into frameBuffer
frameBuffer[frame] = audioData[sampleNumber];
sampleNumber++;
if(sampleNumber > frameTotalForSound) {
soundStruct->isPlaying = false;
AudioOutputUnitStop(soundStruct->outputUnit);
}
}
}
soundStruct->sampleNumber = sampleNumber;
return noErr;
}
是否可以从音频数据的UInt32数组中获取Sint16左右声道样本?
让audioData
和frameBuffer
都为SInt16
s:
SInt16 *audioData;
// ...
SInt16 *frameBuffer;
您的缓冲区大小计算应 n * 2 * sizeof(SInt16) and you'll either need to change
soundStruct` 或添加类型转换。
然后您可以像这样访问交错样本:
frameBuffer[0] = modify(audioData[0]); // left sample 1
frameBuffer[1] = modify(audioData[1]); // right sample 1
frameBuffer[2] = modify(audioData[2]); // left sample 2
frameBuffer[3] = modify(audioData[3]); // right sample 2
// ...
frameBuffer[2*(n-1)] = modify(audioData[2*(n-1)]); // left sample n
frameBuffer[2*(n-1)+1] = modify(audioData[2*(n-1)+1]); // right sample n
@Rhythmic Fistman,非常感谢 - 它很有帮助。
我无法设置 frameBuffer
以这种方式工作。声音在输出端失真。
我猜这是因为 AudioUnit 期望在一帧中包含两个通道数据。或者也许还有其他解释。
这是我修改的代码,希望对大家有所帮助:
audioData
初始化:
SInt16 *audioData = (SInt16 *) malloc (sizeof(SInt16) * totalFramesInFile * 2);
修改渲染回调:
OSStatus MyCallback (void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
ViewController *view = (__bridge ViewController *) inRefCon;
soundStruct *soundStruct = (soundStruct *) &view->mys;
SInt64 frameTotalForSound = soundStruct->frameCount;
soundStruct->isPlaying = true;
SInt16 *audioData = soundStruct->audioData;
UInt32 sampleNumber = soundStruct->sampleNumber;
for( int i = 0; i < ioData->mNumberBuffers; i++){
AudioBuffer buffer = ioData->mBuffers[i];
SInt16 *frameBuffer = (SInt16*) ioData->mBuffers[0].mData;
for(UInt32 frame = 0; frame < inNumberFrames * 2; frame+=2) {
/* .. some samples modification code .. */
// left channel
frameBuffer[frame] = audioData[sampleNumber];
// right channel
frameBuffer[frame + 1] = audioData[sampleNumber + 1];
sampleNumber +=2;
if(sampleNumber > frameTotalForSound * 2) {
soundStruct->isPlaying = false;
AudioOutputUnitStop(soundStruct->outputUnit);
}
}
}
soundStruct->sampleNumber = sampleNumber;
return noErr;
}