Using CMSampleTimingInfo, CMSampleBuffer and AudioBufferList from raw PCM 16000 sample rate stream - ios

I recevie audio data and size from outside, the audio appears to be linear PCM, signed int16, but when recording this using an AssetWriter it saves to the audio file highly distorted and higher pitch.
#define kSamplingRate 16000
#define kNumberChannels 1
UInt32 framesAlreadyWritten = 0;
-(AudioStreamBasicDescription) getAudioFormat {
AudioStreamBasicDescription format;
format.mSampleRate = kSamplingRate;
format.mFormatID = kAudioFormatLinearPCM;
format.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
format.mChannelsPerFrame = 1; // mono
format.mBitsPerChannel = 16;
format.mBytesPerFrame = sizeof(SInt16);
format.mFramesPerPacket = 1;
format.mBytesPerPacket = format.mBytesPerFrame * format.mFramesPerPacket;
format.mReserved = 0;
return format;
}
- (CMSampleBufferRef)createAudioSample:(const void *)audioData frames: (UInt32)len {
AudioStreamBasicDescription asbd = [self getAudioFormat];
CMSampleBufferRef buff = NULL;
static CMFormatDescriptionRef format = NULL;
OSStatus error = 0;
if(format == NULL) {
AudioChannelLayout acl;
bzero(&acl, sizeof(acl));
acl.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
error = CMAudioFormatDescriptionCreate(kCFAllocatorDefault, &asbd, sizeof(acl), &acl, 0, NULL, NULL, &format);
}
CMTime duration = CMTimeMake(1, kSamplingRate);
CMTime pts = CMTimeMake(framesAlreadyWritten, kSamplingRate);
NSLog(#"-----------pts");
CMTimeShow(pts);
CMSampleTimingInfo timing = {duration , pts, kCMTimeInvalid };
error = CMSampleBufferCreate(kCFAllocatorDefault, NULL, false, NULL, NULL, format, len, 1, &timing, 0, NULL, &buff);
framesAlreadyWritten += len;
if (error) {
NSLog(#"CMSampleBufferCreate returned error: %ld", (long)error);
return NULL;
}
AudioBufferList audioBufferList;
audioBufferList.mNumberBuffers = 1;
audioBufferList.mBuffers[0].mNumberChannels = asbd.mChannelsPerFrame;
audioBufferList.mBuffers[0].mDataByteSize = (UInt32)(number_of_frames * audioFormat.mBytesPerFrame);
audioBufferList.mBuffers[0].mData = audioData;
error = CMSampleBufferSetDataBufferFromAudioBufferList(buff, kCFAllocatorDefault, kCFAllocatorDefault, 0, &audioBufferList);
if(error) {
NSLog(#"CMSampleBufferSetDataBufferFromAudioBufferList returned error: %ld", (long)error);
return NULL;
}
return buff;
}

Not sure why you're dividing len by two, but your time should progress instead of being constant, something like
CMTime time = CMTimeMake(framesAlreadyWritten , kSamplingRate);

Related

How to resemple pcm data in iOS

I want to use AudioConverterFillComplexBuffer to convert sample rate for a pcm buffer(32k to 44.1k)。But i didn't know why the voice seems changed(too many noise)。Here is the main code:
struct AudioFrame {
int samples; //number of samples in this frame. e.g. 320
int bytesPerSample; //number of bytes per sample: 2 for PCM16.
int channels; //number of channels (data are interleaved if stereo)
int samplesPerSec; //sampling rate
void* buffer; //data buffer
};
-(void)convertAudioFrame:(AudioFrame *)buffer outPutData:(unsigned char **)outPutData outPutDataSize:(UInt32 *)outPutDataSize{
if (buffer->bytesPerSample != self.unitDescription.mBitsPerChannel ||
buffer->channels != self.unitDescription.mChannelsPerFrame ||
buffer->samplesPerSec != self.unitDescription.mSampleRate){
// describe the input format's description
AudioStreamBasicDescription inputDescription = {0};
inputDescription.mFormatID = kAudioFormatLinearPCM;
inputDescription.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger;
inputDescription.mChannelsPerFrame = buffer->channels;
inputDescription.mSampleRate = buffer->samplesPerSec;
inputDescription.mBitsPerChannel = 16;
inputDescription.mBytesPerFrame = (inputDescription.mBitsPerChannel/8) * inputDescription.mChannelsPerFrame;
inputDescription.mFramesPerPacket = 1;
inputDescription.mBytesPerPacket = inputDescription.mBytesPerFrame;
AudioStreamBasicDescription outputDescription = {0};
outputDescription.mSampleRate = 44100;
outputDescription.mFormatID = kAudioFormatLinearPCM;
outputDescription.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
outputDescription.mChannelsPerFrame = 1;
outputDescription.mFramesPerPacket = 1;
outputDescription.mBitsPerChannel = 16;
outputDescription.mBytesPerFrame = (outputDescription.mBitsPerChannel/8) * outputDescription.mChannelsPerFrame;
outputDescription.mBytesPerPacket = outputDescription.mBytesPerFrame;
// create an audio converter
AudioConverterRef audioConverter;
OSStatus status = AudioConverterNew(&inputDescription, &outputDescription, &audioConverter);
[self checkError:status errorMsg:#"AudioConverterNew error"];
if(!audioConverter)
{
*outPutDataSize = 0;
return;
}
UInt32 outputBytes = outputDescription.mBytesPerPacket * (buffer->samples*buffer->bytesPerSample / inputDescription.mBytesPerPacket);
unsigned char *outputBuffer = (unsigned char*)malloc(outputBytes);
memset(outputBuffer, 0, outputBytes);
AudioBuffer inputBuffer;
inputBuffer.mNumberChannels = inputDescription.mChannelsPerFrame;
inputBuffer.mDataByteSize = buffer->samples*buffer->bytesPerSample;
inputBuffer.mData = buffer->buffer;
AudioBufferList outputBufferList;
outputBufferList.mNumberBuffers = 1;
outputBufferList.mBuffers[0].mNumberChannels = outputDescription.mChannelsPerFrame;
outputBufferList.mBuffers[0].mDataByteSize = outputBytes;
outputBufferList.mBuffers[0].mData = outputBuffer;
UInt32 outputDataPacketSize = outputBytes / outputDescription.mBytesPerPacket;
self.currentBuffer = &inputBuffer;
self.currentInputDescription = inputDescription;
// convert
OSStatus result = AudioConverterFillComplexBuffer(audioConverter,
converterComplexInputDataProc,
(__bridge void*)self,
&outputDataPacketSize,
&outputBufferList,
NULL);
[self checkError:result errorMsg:#"AudioConverterConvertBuffer error"];
*outPutData = outputBuffer;
*outPutDataSize = outputBytes;
AudioConverterDispose(audioConverter);
}
}
//convert callback
OSStatus converterComplexInputDataProc(AudioConverterRef inAudioConverter,
UInt32* ioNumberDataPackets, AudioBufferList* ioData, AudioStreamPacketDescription** ioDataPacketDescription, void* inUserData){
XMMicAudioManager *self = (__bridge XMMicAudioManager *)inUserData;
ioData->mNumberBuffers = 1;
ioData->mBuffers[0] = *(self.currentBuffer);
*ioNumberDataPackets = ioData->mBuffers[0].mDataByteSize / self.currentInputDescription.mBytesPerPacket;
return 0;
}

in iOS Entended Audio File Services and Streaming Audio

First Export from iPod Library m4a file.
Second Convert to AAC using Extended Audio File Services
Third transport the music file to other iPhone.
Fourth Streaming audio using Audio File Stream Services
It usually works.But the audio file playtime over about 5minitues it can't work.
However,I try 15minitues audio file mp3 and aac into my project.and streaming it.it worked.
So,I think the cause is Convert Step. Something wrong with my code ?
Could you give me some advices ?
-(void)convertFrom:(NSURL*)fromURL
toURL:(NSURL*)toURL{
ExtAudioFileRef infile,outfile;
OSStatus err;
//ExtAudioFileの作成
err = ExtAudioFileOpenURL((__bridge CFURLRef)fromURL, &infile);
checkError(err,"ExtAudioFileOpenURL");
AudioStreamBasicDescription inputFormat;
AudioStreamBasicDescription outputFormat;
AVAudioSession * audiosession =[AVAudioSession sharedInstance];
[audiosession setActive:YES error:nil];
[audiosession setCategory:AVAudioSessionCategoryAudioProcessing error:nil];
//変換するフォーマット(AAC)
memset(&outputFormat, 0, sizeof(AudioStreamBasicDescription));
outputFormat.mSampleRate = 44100.0;
outputFormat.mFormatID = kAudioFormatMPEG4AAC;//AAC
outputFormat.mChannelsPerFrame = 1;
UInt32 size = sizeof(AudioStreamBasicDescription);
AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
0, NULL,
&size,
&outputFormat);//変換後のフォーマット
err = ExtAudioFileGetProperty(infile,//変換前のファイルのプロパティを取得
kExtAudioFileProperty_FileDataFormat,
&size,
&inputFormat);
checkError(err,"ExtAudioFileGetProperty");
//リニアPCM以外からの変換であれば、リニアPCMとして読み込む
if(inputFormat.mFormatID != kAudioFormatLinearPCM){
//一旦変換するフォーマット(リニアPCM Little Endian)
AudioStreamBasicDescription linearPCMFormat;
linearPCMFormat.mSampleRate = outputFormat.mSampleRate;
linearPCMFormat.mFormatID = kAudioFormatLinearPCM;
linearPCMFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
linearPCMFormat.mFramesPerPacket = 1;
linearPCMFormat.mChannelsPerFrame = outputFormat.mChannelsPerFrame;
linearPCMFormat.mBitsPerChannel = 16;
linearPCMFormat.mBytesPerPacket = 2 * outputFormat.mChannelsPerFrame;
linearPCMFormat.mBytesPerFrame = 2 * outputFormat.mChannelsPerFrame;
linearPCMFormat.mReserved = 0;
//読み出すフォーマットをリニアPCMにする(中間フォーマット)
inputFormat = linearPCMFormat;
}
//読み込むフォーマットを設定
//必ずlinearPCMで読み出される
err = ExtAudioFileSetProperty(infile,
kExtAudioFileProperty_ClientDataFormat,
sizeof(AudioStreamBasicDescription),
&inputFormat);
checkError(err,"ExtAudioFileSetProperty");
err = ExtAudioFileCreateWithURL((__bridge CFURLRef)toURL,
kAudioFileM4AType, //AAC
&outputFormat,
NULL,
kAudioFileFlags_EraseFile,
&outfile);
checkError(err,"ExtAudioFileCreateWithURL");
//書き込むファイルに、入力がリニアPCMであることを設定
err = ExtAudioFileSetProperty(outfile,
kExtAudioFileProperty_ClientDataFormat,
sizeof(AudioStreamBasicDescription),
&inputFormat);
checkError(err,"kExtAudioFileProperty_ClientDataFormat");
//読み込み位置を0に移動
err = ExtAudioFileSeek(infile, 0);
checkError(err,"ExtAudioFileSeek");
//一度に読み込むフレーム数
UInt32 readFrameSize = 1024;
//読み込むバッファ領域を確保
UInt32 bufferByteSize = sizeof(char) * readFrameSize * inputFormat.mBytesPerPacket;
char *buffer = malloc(bufferByteSize);
//AudioBufferListの作成
AudioBufferList audioBufferList;
audioBufferList.mNumberBuffers = 1;
audioBufferList.mBuffers[0].mNumberChannels = inputFormat.mChannelsPerFrame;
audioBufferList.mBuffers[0].mDataByteSize = bufferByteSize;
audioBufferList.mBuffers[0].mData = buffer;
while(1){
UInt32 numPacketToRead = readFrameSize;
err = ExtAudioFileRead(infile, &numPacketToRead, &audioBufferList);
checkError(err,"ExtAudioFileRead");
//読み込むフレームが無くなったら終了する
if(numPacketToRead == 0){
NSLog(#"変換完了");
break;
}
err = ExtAudioFileWrite(outfile,
numPacketToRead,
&audioBufferList);
checkError(err,"ExtAudioFileWrite");
}
ExtAudioFileDispose(infile);
ExtAudioFileDispose(outfile);
free(buffer);
void propertyListenerProc(
void * inClientData,
AudioFileStreamID inAudioFileStream,
AudioFileStreamPropertyID inPropertyID,
UInt32 * ioFlags
){
StreamInfo* streamInfo = (StreamInfo*)inClientData;
OSStatus err;
NSLog(#"property%u",(unsigned int)inPropertyID);

Output is not generated AudioConverterFillComplexBuffer to convert from AAC to PCM?

Hi I am trying to convert AAC buffer to PCM using AudioConverterFillComplexBuffer..Here is my code
-(void)initDecoder{
AudioStreamBasicDescription outAudioStreamBasicDescription;
outAudioStreamBasicDescription.mSampleRate = 44100.0;
outAudioStreamBasicDescription.mFormatID = kAudioFormatLinearPCM;
outAudioStreamBasicDescription.mFormatFlags = 0xc;
outAudioStreamBasicDescription.mBytesPerPacket = 2;
outAudioStreamBasicDescription.mFramesPerPacket = 1;
outAudioStreamBasicDescription.mBytesPerFrame = 2;
outAudioStreamBasicDescription.mChannelsPerFrame = 1;
outAudioStreamBasicDescription.mBitsPerChannel = 16;
AudioStreamBasicDescription inAudioStreamBasicDescription;
inAudioStreamBasicDescription.mSampleRate = 44100;
inAudioStreamBasicDescription.mFormatID = kAudioFormatMPEG4AAC;
inAudioStreamBasicDescription.mFormatFlags = kMPEG4Object_AAC_SSR;
inAudioStreamBasicDescription.mBytesPerPacket = 0;
inAudioStreamBasicDescription.mFramesPerPacket = 1024;
inAudioStreamBasicDescription.mBytesPerFrame = 0;
inAudioStreamBasicDescription.mChannelsPerFrame = 1;
inAudioStreamBasicDescription.mBitsPerChannel = 0;
inAudioStreamBasicDescription.mReserved = 0;
AudioClassDescription audioClassDescription;
memset(&audioClassDescription, 0, sizeof(audioClassDescription));
audioClassDescription.mManufacturer = kAppleSoftwareAudioCodecManufacturer;
audioClassDescription.mSubType = outAudioStreamBasicDescription.mFormatID;
audioClassDescription.mType = kAudioFormatLinearPCM;
NSAssert(audioClassDescription.mSubType == outAudioStreamBasicDescription.mFormatID && audioClassDescription.mManufacturer == kAppleSoftwareAudioCodecManufacturer, nil);
NSAssert(AudioConverterNewSpecific(&inAudioStreamBasicDescription, &outAudioStreamBasicDescription, 1, &audioClassDescription, &audioConverterDecode) == 0, nil);
}
OSStatus inInputDataProc(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData)
{
AudioBufferList audioBufferList = *(AudioBufferList *)inUserData;
ioData->mBuffers[0].mData = audioBufferList.mBuffers[0].mData;
ioData->mBuffers[0].mDataByteSize = audioBufferList.mBuffers[0].mDataByteSize;
return noErr;
}
-(void)decodeSample:(AudioBufferList)inAaudioBufferList{
//inAaudioBufferList is the AAC buffer
if (!audioConverterDecode) {
[self initDecoder];
}
NSAssert(inAaudioBufferList.mNumberBuffers == 1, nil);
uint32_t bufferSize = 1024*2;//inAaudioBufferList.mBuffers[0].mDataByteSize;
uint8_t *buffer = (uint8_t *)malloc(1024*2);
memset(buffer, 0, bufferSize);
AudioBufferList outAudioBufferList;
outAudioBufferList.mNumberBuffers = 1;
outAudioBufferList.mBuffers[0].mNumberChannels = 1;
outAudioBufferList.mBuffers[0].mDataByteSize = bufferSize;
outAudioBufferList.mBuffers[0].mData = buffer;
UInt32 ioOutputDataPacketSize = bufferSize;
OSStatus ret = AudioConverterFillComplexBuffer(audioConverterDecode, inInputDataProc, &inAaudioBufferList, &ioOutputDataPacketSize, &outAudioBufferList, NULL) ;//== 0, nil);
NSData *data = [NSData dataWithBytes:outAudioBufferList.mBuffers[0].mData length:outAudioBufferList.mBuffers[0].mDataByteSize];
DLog(#"Rev Size = %d",(unsigned int)outAudioBufferList.mBuffers[0].mDataByteSize);
free(buffer);
}
The decoded output length is zero and the OSStatus code for AudioConverterFillComplexBuffer is 561015652
So what could be wrong..?
This is a shot in the dark and you probably already found the solution to this but I believe you need to change this
UInt32 ioOutputDataPacketSize = bufferSize;
to this
UInt32 ioOutputDataPacketSize = bufferSize/2;
Personally for me I do this in the inInputDataProc method and always sent
UInt32 ioOutputDataPacketSize = 1;
into the AudioConverterFillComplexBuffer method and then set it within the inInputDataProc like this.
UInt32 maxPackets = audioBufferList.mBuffers[0].mDataByteSize / 2;
*ioNumberDataPackets = maxPackets;
Hope this helps.

AudioConverterNew return OSStatus fmt

i'm trying to convert pcm audio got from AVAudioCapture to aac format. i use AudioConverterNew.But i always got an OSStatus 1718449215, i know it means kAudioFormatUnsupportedDataFormatError, but i don't know why, here's my code
OSStatus status;
AudioStreamBasicDescription sourceASBD = *sourceFormat;
AudioStreamBasicDescription destASBD = {0};
destASBD.mSampleRate = sourceASBD.mSampleRate;
destASBD.mFormatID = kAudioFormatMPEG4AAC;
destASBD.mChannelsPerFrame = 1;
UInt32 size = sizeof(destASBD);
status = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &destASBD);
if (status != noErr) {
NSLog(#"AudoFormatGetProperty kAudioFormatProperty_FormatInfo error:%d", status);
}
AudioClassDescription *description = [self
getAudioClassDescriptionWithType:destASBD.mFormatID
fromManufacturer:kAppleSoftwareAudioCodecManufacturer];
if (!description) {
NSLog(#"get audio class Descriptin error");
return false;
}
AudioConverterReset(_converter);
status = AudioConverterNewSpecific(&sourceASBD, &destASBD, 1, description, &_converter);
if (status != noErr) {
NSLog(#"create audo converter error:%d", status);
return false;
}
i got the sourceFormat from CMSampleBufferRef, anyone know what's the problem? the sourceASDB is
mSampleRate = 44100
mFormatID = 1819304813 linearpcm
mFormatFlags = 12
mBytesPerPacket = 2
mFramesPerPacket = 1
mBytesPerFrame = 2
mChannelsPerFrame = 1
mBitsPerChannel = 16
mReserved = 0

How can I modify this AudioUnit code so that it has stereo output?

I can't seem to find what I'm looking for in the documentation. This code works great, but I want stereo output.
- (void)createToneUnit
{
// Configure the search parameters to find the default playback output unit
// (called the kAudioUnitSubType_RemoteIO on iOS but
// kAudioUnitSubType_DefaultOutput on Mac OS X)
AudioComponentDescription defaultOutputDescription;
defaultOutputDescription.componentType = kAudioUnitType_Output;
defaultOutputDescription.componentSubType = kAudioUnitSubType_RemoteIO;
defaultOutputDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
defaultOutputDescription.componentFlags = 0;
defaultOutputDescription.componentFlagsMask = 0;
// Get the default playback output unit
AudioComponent defaultOutput = AudioComponentFindNext(NULL, &defaultOutputDescription);
NSAssert(defaultOutput, #"Can't find default output");
// Create a new unit based on this that we'll use for output
OSErr err = AudioComponentInstanceNew(defaultOutput, &_toneUnit);
NSAssert1(_toneUnit, #"Error creating unit: %d", err);
// Set our tone rendering function on the unit
AURenderCallbackStruct input;
input.inputProc = RenderTone;
input.inputProcRefCon = (__bridge void*)self;
err = AudioUnitSetProperty(_toneUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&input,
sizeof(input));
NSAssert1(err == noErr, #"Error setting callback: %d", err);
// Set the format to 32 bit, single channel, floating point, linear PCM
const int four_bytes_per_float = 4;
const int eight_bits_per_byte = 8;
AudioStreamBasicDescription streamFormat;
streamFormat.mSampleRate = kSampleRate;
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags =
kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
streamFormat.mBytesPerPacket = four_bytes_per_float;
streamFormat.mFramesPerPacket = 1;
streamFormat.mBytesPerFrame = four_bytes_per_float;
streamFormat.mChannelsPerFrame = 1;
streamFormat.mBitsPerChannel = four_bytes_per_float * eight_bits_per_byte;
err = AudioUnitSetProperty (_toneUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&streamFormat,
sizeof(AudioStreamBasicDescription));
NSAssert1(err == noErr, #"Error setting stream format: %dd", err);
}
And here is the callback:
OSStatus RenderTone( void* inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData){
// Get the tone parameters out of the view controller
VWWSynthesizerC *synth = (__bridge VWWSynthesizerC *)inRefCon;
double theta = synth.theta;
double theta_increment = 2.0 * M_PI * synth.frequency / kSampleRate;
// This is a mono tone generator so we only need the first buffer
const int channel = 0;
Float32 *buffer = (Float32 *)ioData->mBuffers[channel].mData;
// Generate the samples
for (UInt32 frame = 0; frame < inNumberFrames; frame++)
{
if(synth.muted){
buffer[frame] = 0;
}
else{
switch(synth.waveType){
case VWWWaveTypeSine:{
buffer[frame] = sin(theta) * synth.amplitude;
break;
}
case VWWWaveTypeSquare:{
buffer[frame] = square(theta) * synth.amplitude;
break;
}
case VWWWaveTypeSawtooth:{
buffer[frame] = sawtooth(theta) * synth.amplitude;
break;
}
case VWWWaveTypeTriangle:{
buffer[frame] = triangle(theta) * synth.amplitude;
break;
}
default:
break;
}
}
theta += theta_increment;
if (theta > 2.0 * M_PI)
{
theta -= 2.0 * M_PI;
}
}
synth.theta = theta;
return noErr;
}
If there is a different or better way to render this data, I'm open to suggestions. I'm rendering sine, square, triangle, sawtooth, etc... waves.

Resources