gpt4 book ai didi

ios - 核心音频 : Float32 to SInt16 conversion artefacts

转载 作者:行者123 更新时间:2023-11-28 21:47:23 24 4
gpt4 key购买 nike

我正在从以下格式转换:

const int four_bytes_per_float = 4;
const int eight_bits_per_byte = 8;
_stereoGraphStreamFormat.mFormatID = kAudioFormatLinearPCM;
_stereoGraphStreamFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
_stereoGraphStreamFormat.mBytesPerPacket = four_bytes_per_float;
_stereoGraphStreamFormat.mFramesPerPacket = 1;
_stereoGraphStreamFormat.mBytesPerFrame = four_bytes_per_float;
_stereoGraphStreamFormat.mChannelsPerFrame = 2;
_stereoGraphStreamFormat.mBitsPerChannel = eight_bits_per_byte * four_bytes_per_float;
_stereoGraphStreamFormat.mSampleRate = 44100;

格式如下:

interleavedAudioDescription.mFormatID          = kAudioFormatLinearPCM;
interleavedAudioDescription.mFormatFlags = kAudioFormatFlagIsSignedInteger;
interleavedAudioDescription.mChannelsPerFrame = 2;
interleavedAudioDescription.mBytesPerPacket = sizeof(SInt16)*interleavedAudioDescription.mChannelsPerFrame;
interleavedAudioDescription.mFramesPerPacket = 1;
interleavedAudioDescription.mBytesPerFrame = sizeof(SInt16)*interleavedAudioDescription.mChannelsPerFrame;
interleavedAudioDescription.mBitsPerChannel = 8 * sizeof(SInt16);
interleavedAudioDescription.mSampleRate = 44100;

使用以下代码:

int32_t availableBytes = 0;

void* tailL = TPCircularBufferTail(inputBufferL(), &availableBytes);
void* tailR = TPCircularBufferTail(inputBufferR(), &availableBytes);

// If we have no data in the buffer, we simply return
if (availableBytes <= 0)
{
return;
}

// ========== Non-Interleaved to Interleaved (Plus Samplerate Conversion) =========

// Get the number of frames available
UInt32 frames = availableBytes / this->mInputFormat.mBytesPerFrame;

pcmOutputBuffer->mBuffers[0].mDataByteSize = frames * interleavedAudioDescription.mBytesPerFrame;

struct complexInputDataProc_t data = (struct complexInputDataProc_t) { .self = this, .sourceL = tailL, .sourceR = tailR, .byteLength = availableBytes };

// Do the conversion
OSStatus result = AudioConverterFillComplexBuffer(interleavedAudioConverter,
complexInputDataProc,
&data,
&frames,
pcmOutputBuffer,
NULL);

// Tell the buffers how much data we consumed during the conversion so that it can be removed
TPCircularBufferConsume(inputBufferL(), availableBytes);
TPCircularBufferConsume(inputBufferR(), availableBytes);

// ========== Buffering Of Interleaved Samples =========

// If we got converted frames back from the converter, we want to add it to a separate buffer
if (frames > 0)
{
// Make sure we have enough space in the buffer to store the new data
TPCircularBufferHead(&pcmCircularBuffer, &availableBytes);

if (availableBytes > pcmOutputBuffer->mBuffers[0].mDataByteSize)
{
// Add the newly converted data to the buffer
TPCircularBufferProduceBytes(&pcmCircularBuffer, pcmOutputBuffer->mBuffers[0].mData, frames * interleavedAudioDescription.mBytesPerFrame);
}
else
{
printf("No Space in Buffer\n");
}
}

但是我得到以下输出:

Sine Wave After Conversion

它应该是一个完美的正弦波,但正如您所见,它不是。

我已经为此工作好几天了,但似乎找不到哪里出了问题。谁能看到我可能遗漏的东西?

编辑:

缓冲区初始化:

TPCircularBuffer        pcmCircularBuffer;
static SInt16 pcmOutputBuf[BUFFER_SIZE];

pcmOutputBuffer = (AudioBufferList*)malloc(sizeof(AudioBufferList));
pcmOutputBuffer->mNumberBuffers = 1;
pcmOutputBuffer->mBuffers[0].mNumberChannels = 2;
pcmOutputBuffer->mBuffers[0].mData = pcmOutputBuf;

TPCircularBufferInit(&pcmCircularBuffer, BUFFER_SIZE);

复杂的输入数据过程:

static OSStatus complexInputDataProc(AudioConverterRef             inAudioConverter,
UInt32 *ioNumberDataPackets,
AudioBufferList *ioData,
AudioStreamPacketDescription **outDataPacketDescription,
void *inUserData) {

struct complexInputDataProc_t *arg = (struct complexInputDataProc_t*)inUserData;
BroadcastingServices::MP3Encoder *self = (BroadcastingServices::MP3Encoder*)arg->self;

if ( arg->byteLength <= 0 )
{
*ioNumberDataPackets = 0;
return 100; //kNoMoreDataErr;
}

UInt32 framesAvailable = arg->byteLength / self->interleavedAudioDescription.mBytesPerFrame;

if (*ioNumberDataPackets > framesAvailable)
{
*ioNumberDataPackets = framesAvailable;
}

ioData->mBuffers[0].mData = arg->sourceL;
ioData->mBuffers[0].mDataByteSize = arg->byteLength;

ioData->mBuffers[1].mData = arg->sourceR;
ioData->mBuffers[1].mDataByteSize = arg->byteLength;

arg->byteLength = 0;

return noErr;

最佳答案

我看到了一些值得警惕的事情。

1) 如上面的评论所述,您正在用右侧的输入覆盖左侧输入的 availableBytes:

void* tailL = TPCircularBufferTail(inputBufferL(), &availableBytes);
void* tailR = TPCircularBufferTail(inputBufferR(), &availableBytes);

如果两个输入流与此代码异步更改,那么您很可能会遇到竞争条件。

2) 截断错误:availableBytes 不一定是每帧字节数的倍数。如果不是,则以下代码可能会导致您消耗比实际转换更多的字节。

void* tailL = TPCircularBufferTail(inputBufferL(), &availableBytes);
void* tailR = TPCircularBufferTail(inputBufferR(), &availableBytes);
...
UInt32 frames = availableBytes / this->mInputFormat.mBytesPerFrame;
...
TPCircularBufferConsume(inputBufferL(), availableBytes);
TPCircularBufferConsume(inputBufferR(), availableBytes);

3) 如果输出缓冲区还没有准备好消耗所有输入,您只需将输入缓冲区扔掉。这发生在这段代码中。

if (availableBytes > pcmOutputBuffer->mBuffers[0].mDataByteSize)
{
...
}
else
{
printf("No Space in Buffer\n");
}

如果您看到打印输出,我会非常好奇。

这是我建议的做法。这将是伪代码,因为我没有任何必要的东西来编译和测试它。

int32_t availableBytesInL = 0;
int32_t availableBytesInR = 0;
int32_t availableBytesOut = 0;

// figure out how many bytes are available in each buffer.
void* tailL = TPCircularBufferTail(inputBufferL(), &availableBytesInL);
void* tailR = TPCircularBufferTail(inputBufferR(), &availableBytesInR);
TPCircularBufferHead(&pcmCircularBuffer, &availableBytesOut);

// figure out how many full frames are available
UInt32 framesInL = availableBytesInL / mInputFormat.mBytesPerFrame;
UInt32 framesInR = availableBytesInR / mInputFormat.mBytesPerFrame;
UInt32 framesOut = availableBytesOut / interleavedAudioDescription.mBytesPerFrame;

// figure out how many frames to process this time.
UInt32 frames = min(min(framesInL, framesInL), framesOut);

if (frames == 0)
return;

int32_t bytesConsumed = frames * mInputFormat.mBytesPerFrame;

struct complexInputDataProc_t data = (struct complexInputDataProc_t) {
.self = this, .sourceL = tailL, .sourceR = tailR, .byteLength = bytesConsumed };

// Do the conversion
OSStatus result = AudioConverterFillComplexBuffer(interleavedAudioConverter,
complexInputDataProc,
&data,
&frames,
pcmOutputBuffer,
NULL);

int32_t bytesProduced = frames * interleavedAudioDescription.mBytesPerFrame;

// Tell the buffers how much data we consumed during the conversion so that it can be removed
TPCircularBufferConsume(inputBufferL(), bytesConsumed);
TPCircularBufferConsume(inputBufferR(), bytesConsumed);
TPCircularBufferProduceBytes(&pcmCircularBuffer, pcmOutputBuffer->mBuffers[0].mData, bytesProduced);

基本上我在这里所做的是预先计算出应该处理多少帧,确保我只处理输出缓冲区可以处理的帧数。如果是我,我还会添加一些检查输出缓冲区欠载和输入缓冲区溢出的检查。最后,我不太确定 AudioConverterFillComplexBuffer 与传入和传出的 frame 参数的语义。可以想象出的 # 帧数将少于或多于输入的帧数。尽管如此,由于您没有进行采样率转换,所以这可能不会发生。我试图通过在转换后分配 bytesProduced 来解释这种情况。

希望这对您有所帮助。如果不是,您还有其他 2 条线索。一是辍学是周期性的,二是辍学的规模看起来大致相同。如果您能算出每个样本有多少个样本,那么您就可以在代码中查找这些数字。

关于ios - 核心音频 : Float32 to SInt16 conversion artefacts,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29619205/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com