ExtAudioFileRead gives different result in iOS 5 - objective-c

I'm trying to extract Linear PCM data from a MP3 file.
Before iOS 5, I could do it successfully using AudioToolbox framework, specifically ExtAudioFileRead function.
However, in iOS 5 the ExtAudioFileRead function gives completely different result from that in iOS 4.
First, it cannot read all the packets in the source MP3 file.
For example, it reads only 1637 packets while the source MP3 file has 2212 packets in total.
Second, the PCM values obtained from the function are completely different from those obtained in iOS 4.
I can't figure out what I did wrong :(
The same framework, the same function, and the same code... but completely different results?
I doubt it is a bug of iOS 5 so I reported the problem to Apple already.
But Apple has not answer to my bug reporting for 2 weeks!
Here's the code that causes the problem.
After executing the code, I expect to have the correct PCM data in pcmBuffer.
In iOS 4, the code gives the result what I expected to.
But in iOS 5, the result is completely different and wrong.
Please, somebody help me!
OSStatus status;
ExtAudioFileRef fileRef;
CFURLRef fileURL = (CFURLRef)[NSURL fileURLWithPath:filePath];
status = ExtAudioFileOpenURL((CFURLRef)fileURL, &fileRef);
AudioStreamBasicDescription dataFormat;
dataFormat.mSampleRate = SAMPLE_RATE;
dataFormat.mFormatID = kAudioFormatLinearPCM;
dataFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
dataFormat.mFramesPerPacket = 1;
dataFormat.mChannelsPerFrame = 1;
dataFormat.mBitsPerChannel = 16;
dataFormat.mBytesPerPacket = 2;
dataFormat.mBytesPerFrame = 2;
UInt32 propDataSize;
AudioStreamBasicDescription originalDataFormat;
propDataSize = (UInt32)sizeof(originalDataFormat);
status = ExtAudioFileGetProperty(fileRef, kExtAudioFileProperty_FileDataFormat, &propDataSize, &originalDataFormat);
SInt64 numPackets;
propDataSize = sizeof(numPackets);
status = ExtAudioFileGetProperty(fileRef, kExtAudioFileProperty_FileLengthFrames, &propDataSize, &numPackets);
propDataSize = (UInt32)sizeof(dataFormat);
status = ExtAudioFileSetProperty(fileRef, kExtAudioFileProperty_ClientDataFormat, propDataSize, &dataFormat);
numPackets = (SInt64)numPackets / (SInt64)(originalDataFormat.mSampleRate / SAMPLE_RATE);
size_t bufferSize = (size_t)(numPackets * sizeof(SInt16));
SInt16 *pcmBuffer = (SInt16 *)malloc(bufferSize);
AudioBufferList bufList;
bufList.mNumberBuffers = 1;
bufList.mBuffers[0].mNumberChannels = 1;
bufList.mBuffers[0].mDataByteSize = bufferSize;
bufList.mBuffers[0].mData = pcmBuffer;
ExtAudioFileSeek(fileRef, 0);
UInt32 totalFramesRead = 0;
do {
UInt32 framesRead = numPackets - totalFramesRead;
bufList.mBuffers[0].mData = pcmBuffer + (totalFramesRead * (sizeof(SInt16)));
ExtAudioFileRead(fileRef, &framesRead, &bufList);
totalFramesRead += framesRead;
if(framesRead == 0) {
break;
}
NSLog(#"read %lu frames\n", framesRead);
} while (totalFramesRead < numPackets);
int totalPackets = totalFramesRead;
status = ExtAudioFileDispose(fileRef);
NSLog(#"numPackets : %lld, totalPackets : %d", numPackets, totalPackets);

Ouch. I noted that the number is different if the original sampling rate of the song is different. Back to square 1.

Related

Audiotoolbox API AudioConvertNewSpecific returning wrong output packet size when converting to G711u instead of AAC in Objective C

I am trying to convert the RAW PCM buffers to G711u Codec in IOS using Audio Toolbox. I see many code examples working for the AAC codec using the same code and APIs.But when I try to do it for PCMU then it is not working. AudioConverter is setup with no errors but when I try to query the output packet size in AudioConverterGetProperty then it returns value of 2 which seems wrong to me because when I use same code for AAC codec then it returns output packet size of 600. Below here I paste my code and thanks in advance to help me out in this .
=====Source Code ===========
OSStatus result = 0;
AudioStreamBasicDescription in = {0}, out = {0};
in.mSampleRate = frequencyInHz;
in.mChannelsPerFrame = channelCount;
in.mBitsPerChannel = 16;
in.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
in.mFormatID = kAudioFormatLinearPCM;
in.mFramesPerPacket = 1;
in.mBytesPerFrame = in.mBitsPerChannel * in.mChannelsPerFrame / 8;
in.mBytesPerPacket = in.mFramesPerPacket*in.mBytesPerFrame;
m_in = in;
out.mFormatID = kAudioFormatULaw;
out.mFormatFlags = 0;
out.mFramesPerPacket = kSamplesPerFrame;
out.mSampleRate = frequencyInHz;
out.mChannelsPerFrame = channelCount;
m_out = out;
UInt32 outputBitrate = bitrate;
UInt32 propSize = sizeof(outputBitrate);
UInt32 outputPacketSize = 1024;
const OSType subtype = kAudioFormatULaw;
AudioClassDescription requestedCodecs[2] = {
{
kAudioEncoderComponentType,
subtype,
kAppleSoftwareAudioCodecManufacturer
},
{
kAudioEncoderComponentType,
subtype,
kAppleHardwareAudioCodecManufacturer
}
};
result = AudioConverterNewSpecific(&in, &out, 2, requestedCodecs, &m_audioConverter);
if(result !=0) {
DLog("Error AudioConverterNewSpecific %x \n", (int)result);
}
result = AudioConverterSetProperty(m_audioConverter, kAudioConverterEncodeBitRate, propSize, &outputBitrate);
if(result !=0) {
DLog("Error AudioConverterSetProperty %x \n", (int)result);
}
result = AudioConverterGetProperty(m_audioConverter, kAudioConverterPropertyMaximumOutputPacketSize, &propSize, &outputPacketSize);
if(result !=0) {
DLog("Error AudioConverterGetProperty %x \n", (int)result);
}
DLog("The output packet size is %x \n", (int)outputPacketSize);
======Output============
Error AudioConverterSetProperty 70726f70
The output packet size is 2
The problem is that the output packet size is returned to 2 and when I try to use the encoder then it gives me the output in 2 bytes for each call. I think we are not receiving the correct output packet size . How to correct it?

Change Variable over Time Interval in Audio Callback

so I wanted to change my variable within my for loop over a time interval. In my code, I have my audio callback basically running the following pseudocode:
int start = 0, target = 100;
for (int i = 0; i < frames; i++) {
[object makeSineWave];
[object useNum:start];
if (target > start) {
// Increase start over the span of time frame
}
}
What I want to do is increase start to the target value over a logarithmic scale within a time interval (lets say 1 second to keep it simple). How would I keep track of time within the for loop of the audio callback?
EDIT: Guess I'm trying to make a filter sweep... I'm guessing it'd be similar to a sine sweep now that I think about it? more code-
OSStatus RenderTone(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
// Get Audio Data
AudioData *data = (__bridge AudioData *)inRefCon;
static Float32 phs = 0, sub_phs = 0;
Float32 freq = data->freq;
// Calculate phases
Float32 phs_incr = 2 * M_PI * freq / data->srate;
Float32 sample;
// Buffers
Float32 *bufL = (Float32 *)ioData->mBuffers[0].mData;
Float32 *bufR = (Float32 *)ioData->mBuffers[1].mData;
// Start at 1 hz, target is 500 hz
int start = 1, target = 500;
// Generate Samples
for (UInt32 i = 0; i < inNumberFrames; i++) {
// Sine waveform
sample = sinf(phs);
sample = [data->filter processFilter:sample fc:start];
// change start here using current time?
// should not increase to target immediately, over span of 1 second
bufL[i] = buf[R] = sample;
// Increment phase
phs += phs_incr;
// Wrap phase
phs = wrapPhase(phs);
}
}
Seems that I found out how to do it through looking at CCRMA's STK... great resource that I wish I found earlier!
https://ccrma.stanford.edu/software/stk/

How to retrieve the total number of bytes read and written on the boot drive

io_iterator_t drives = IO_OBJECT_NULL;
mach_port_t masterPort = IO_OBJECT_NULL;
IOMasterPort(bootstrap_port, &masterPort);
IOServiceGetMatchingServices(masterPort, IOServiceMatching(kIOBlockStorageDriverClass), &drives);
io_registry_entry_t drive = 0;
uint64_t totalBytesRead = 0;
uint64_t totalBytesWritten = 0;
while ((drive = IOIteratorNext(drives)))
{
io_name_t drivePath;
IORegistryEntryGetPath(drive, kIOServicePlane, drivePath);
NSLog(#"Path:%s", drivePath);
NSNumber* statVal = 0;
NSDictionary* statistics;
statistics = (NSDictionary*)IORegistryEntryCreateCFProperty(drive, CFSTR(kIOBlockStorageDriverStatisticsKey), kCFAllocatorDefault, kNilOptions);
if (statistics)
{
statVal = (NSNumber*)[statistics objectForKey:(NSString*)CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)];
totalBytesRead += [statVal intValue];
statVal = (NSNumber*)[statistics objectForKey:(NSString*)CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)];
totalBytesWritten += [statVal intValue];
}
[statistics release];
IOObjectRelease(drive);
}
IOIteratorReset(drives);
readRate = (double)(totalBytesRead - prevReadVal)/1024;
writeRate = (double)(totalBytesWritten - prevWriteVal)/1024;
prevReadVal = totalBytesRead;
prevWriteVal = totalBytesWritten;
I am trying to retrieve the total number of bytes read and written on the boot drive since startup. However, the above code doesn't seem to work. I did a little investigation and realised that the line: statistics = (NSDictionary*)IORegistryEntryCreateCFProperty(drive, CFSTR(kIOBlockStorageDriverStatisticsKey), kCFAllocatorDefault, kNilOptions); is not working. Instead of returning a valid object, the function seems to return null. Does anyone know why? A comprehensive google search did not yield any results.
Note I am on Mountain Lion OS X using objective C and building an app for OSX.

Setting up effect audio units for CoreAudio

I'm trying to setup a high-pass filter but AUGraphStart gives me -10863 when I try. I cannot find much documntation at all. Here is my attent to set up the filter:
- (void)initializeAUGraph{
AUNode outputNode;
AUNode mixerNode;
AUNode effectNode;
NewAUGraph(&mGraph);
// Create AudioComponentDescriptions for the AUs we want in the graph
// mixer component
AudioComponentDescription mixer_desc;
mixer_desc.componentType = kAudioUnitType_Mixer;
mixer_desc.componentSubType = kAudioUnitSubType_AU3DMixerEmbedded;
mixer_desc.componentFlags = 0;
mixer_desc.componentFlagsMask = 0;
mixer_desc.componentManufacturer = kAudioUnitManufacturer_Apple;
// output component
AudioComponentDescription output_desc;
output_desc.componentType = kAudioUnitType_Output;
output_desc.componentSubType = kAudioUnitSubType_RemoteIO;
output_desc.componentFlags = 0;
output_desc.componentFlagsMask = 0;
output_desc.componentManufacturer = kAudioUnitManufacturer_Apple;
//effect component
AudioComponentDescription effect_desc;
effect_desc.componentType = kAudioUnitType_Effect;
effect_desc.componentSubType = kAudioUnitSubType_HighPassFilter;
effect_desc.componentFlags = 0;
effect_desc.componentFlagsMask = 0;
effect_desc.componentManufacturer = kAudioUnitManufacturer_Apple;
// Add nodes to the graph to hold our AudioUnits
AUGraphAddNode(mGraph, &output_desc, &outputNode);
AUGraphAddNode(mGraph, &mixer_desc, &mixerNode);
AUGraphAddNode(mGraph, &effect_desc, &effectNode);
// Connect the nodes
AUGraphConnectNodeInput(mGraph, mixerNode, 0, effectNode, 0);
AUGraphConnectNodeInput(mGraph, effectNode, 0, outputNode, 0);
//Open Graph
AUGraphOpen(mGraph);
// Get a link to the mixer AU
AUGraphNodeInfo(mGraph, mixerNode, NULL, &mMixer);
// Get a link to the effect AU
AUGraphNodeInfo(mGraph, effectNode, NULL, &mEffect);
//Setup buses
size_t numbuses = track_count;
UInt32 size = sizeof(numbuses);
AudioUnitSetProperty(mMixer, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &numbuses, size);
//Setup Stream Format Data
AudioStreamBasicDescription desc;
size = sizeof(desc);
// Setup Stream Format
desc.mSampleRate = kGraphSampleRate;
desc.mFormatID = kAudioFormatLinearPCM;
desc.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
desc.mBitsPerChannel = sizeof(AudioSampleType) * 8; // AudioSampleType == 16 bit signed ints
desc.mChannelsPerFrame = 1;
desc.mFramesPerPacket = 1;
desc.mBytesPerFrame = sizeof(AudioSampleType);
desc.mBytesPerPacket = desc.mBytesPerFrame;
// Loop through and setup a callback for each source you want to send to the mixer.
for (int i = 0; i < numbuses; ++i) {
// Setup render callback struct
AURenderCallbackStruct renderCallbackStruct;
renderCallbackStruct.inputProc = &renderInput;
renderCallbackStruct.inputProcRefCon = self;
// Connect the callback to the mixer input channel
AUGraphSetNodeInputCallback(mGraph, mixerNode, i, &renderCallbackStruct);
// Apply Stream Data
AudioUnitSetProperty(mMixer, kAudioUnitProperty_StreamFormat,kAudioUnitScope_Input,i,&desc,size);
AudioUnitSetParameter(mMixer, k3DMixerParam_Distance, kAudioUnitScope_Input, i, rand() % 6, 0);
rotation[i] = rand() % 360;
rotation_speed[i] = rand() % 5;
AudioUnitSetParameter(mMixer, k3DMixerParam_Azimuth, kAudioUnitScope_Input, i, rotation[i], 0);
AudioUnitSetParameter(mMixer, k3DMixerParam_Elevation, kAudioUnitScope_Input, i, 30, 0);
}
// Reset stream fromat data to 0
memset (&desc, 0, sizeof (desc));
// Setup output stream format
desc.mSampleRate = kGraphSampleRate;
// Apply Stream Data to Output
AudioUnitSetProperty(mEffect,kAudioUnitProperty_StreamFormat,kAudioUnitScope_Input,0,&desc,size);
AudioUnitSetProperty(mEffect,kAudioUnitProperty_StreamFormat,kAudioUnitScope_Output,0,&desc,size);
AudioUnitSetProperty(mMixer,kAudioUnitProperty_StreamFormat,kAudioUnitScope_Output,0,&desc,size);
//All done so initialise
AUGraphInitialize(mGraph);
}
It works when I remove the high pass filter. How do I get the filter working?
Thank you.
PS: Is the 3D elevation supposed to do nothing?
if u still have the problem...u should add a converter unit between the mixernode and the effect node and set the input format of it as the mixer output and the output format to the format u get from audiounitgetproperty (converternode)
Not all Audio Units that are available on OSX are available on iOS. In fact, only a few are. According to below documentation the highpassfilter effect is not supported on iOS : http://developer.apple.com/library/ios/#documentation/MusicAudio/Conceptual/AudioUnitHostingGuide_iOS/UsingSpecificAudioUnits/UsingSpecificAudioUnits.html#//apple_ref/doc/uid/TP40009492-CH17-SW1
"The iPod EQ unit (subtype kAudioUnitSubType_AUiPodEQ) is the only effect unit provided in iOS 4."
Note that it mentions iOS4. But I am unable to find any documentation on this for later versions of iOS.

How can I modify the SpeakHere sample app to record in mono format on iPhone?

I am new to iPhone. Could you please help me to modify the SpeakHere app from Apple to record in mono format. What should I have to set for mChannelsPerFrame and what else should I set?
I already change some part for record on linearPCM WAVE format.
Here is link to speakHere.
Here is what I think they allow me to change but I don't quite understand on sound:
void ChangeNumberChannels(UInt32 nChannels, bool interleaved)
// alter an existing format
{
Assert(IsPCM(), "ChangeNumberChannels only works for PCM formats");
UInt32 wordSize = SampleWordSize(); // get this before changing ANYTHING
if (wordSize == 0)
wordSize = (mBitsPerChannel + 7) / 8;
mChannelsPerFrame = nChannels;
mFramesPerPacket = 1;
if (interleaved) {
mBytesPerPacket = mBytesPerFrame = nChannels * wordSize;
mFormatFlags &= ~kAudioFormatFlagIsNonInterleaved;
} else {
mBytesPerPacket = mBytesPerFrame = wordSize;
mFormatFlags |= kAudioFormatFlagIsNonInterleaved;
}
}
On iPhone you will only be able to record in mono.
You shouldn't need to do anything to set this up in the SpeakHere example. It's done automatically. For example in AQRecorder::SetupAudioFormat:
size = sizeof(mRecordFormat.mChannelsPerFrame);
XThrowIfError(AudioSessionGetProperty( kAudioSessionProperty_CurrentHardwareInputNumberChannels,
&size,
&mRecordFormat.mChannelsPerFrame), "couldn't get input channel count");
That gets the supported hardware input channels and sets it as an ivar. Elsewhere, the buffer size calculations will factor that in.