I'm developing a bass guitar pitch detection app on iphone 3GS. I found I can't get sound data lower than 150Hz with RemoteIO. However bass guitar may generate tones lower than 50hz. According to the report "iPhone 4 Headset Input Frequency Response", http://blog.faberacoustical.com/2010/iphone/iphone-4-audio-and-frequency-response-limitations/ There is a sharp drop-off below 150 Hz.
Here shows how I setup the AudioUnit.
// set audio unit
{
// create AudioUnit
{
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
AudioComponent comp = AudioComponentFindNext(NULL, &desc);
OSAssert(AudioComponentInstanceNew(comp, &m_AudioUnit));
}
//enable input on the remote I/O unit (output is default enabled, but input is not)
{
UInt32 one = 1;
OSAssert(AudioUnitSetProperty(m_AudioUnit, kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input, 1, &one, sizeof(one)));
}
//set render callback function
{
AURenderCallbackStruct callbackInfo;
callbackInfo.inputProc=staticPerformThru;
callbackInfo.inputProcRefCon=this;
OSAssert(AudioUnitSetProperty(m_AudioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0, &callbackInfo, sizeof(callbackInfo)));
}
//set in/output format
{
CAStreamBasicDescription outFormat;
outFormat.SetAUCanonical(channels, false);
outFormat.mSampleRate = sampleRate;
OSAssert(AudioUnitSetProperty(m_AudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &outFormat, sizeof(outFormat)));
OSAssert(AudioUnitSetProperty(m_AudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &outFormat, sizeof(outFormat)));
}
//Initialize remote I/O unit
OSStatus r=AudioUnitInitialize(m_AudioUnit);
OSAssert(r);
}
//start audio output
OSAssert(AudioOutputUnitStart(m_AudioUnit));
This is the callback function.
OSStatus AudioThruWorker::staticPerformThru(
void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
AudioUnitRender(((AudioThruWorker*)inRefCon)->m_AudioUnit, ioActionFlags, inTimeStamp, 1, inNumberFrames, ioData);
//Detect pitch here...
return 0;
}
To identify the root cause,
- I modified my callback function to just bypass input data to output.
- Use Mac to generate white noise
- Use iRig to redirect the signal from Mac's headset to iPhone3Gs which is running my program.
- Use iRig to redirect output of iPhone back to Mac.
- Record data on Mac. The output data spectrum shows in below figure.
You can see the sharp drop at 150Hz.
To identify whether the problem is at input side or output side, I changed the callback function to ignore input data and output a white noise. Here is the result. Very clear that there is NO drop at 150 Hz. Therefore the problem should at the input side.
I thought this is a hardware limitation. HOWEVER I tried the app "Amplitube" on the same device, turn off all effects ,input white noise and analyze output. It has NO drop at 150Hz. Here is the result. This means the drop-off problem is NOT a hardware limitation. There must be some way software can do to avoid the problem.
Does anyone know the secret?
Thanks.
Well, it is a phone, a device optimized for speech supposedly. And devices optimized for speech usually have some sort of LF cut filter to avoid rumble and distortion.
ReplyDeleteThis filter is probably on the input side of the phone, that is why you can generate and output a wider range of frequencies, it is probably a hardware/discrete element filter, since those are simple to create using only a few components and will work in real time without any strain on processing.
I don't think it makes sense to cut lows in software, I know I wouldn't do it, well, for a DAW application you can do it, but for a device optimized to filter lows...
Considered the fact amplitube devs might have been aware of this issue and added extra low boost to try and make it up for the hardware limitation?
On the other hand, it may very well be possible to chose different "signal paths" according to usage scenario, maybe there is some os handle apps can touch and say "hey, I am not voice, don't cut my lows" - if so, this feature should be somewhere in the api description.
Interesting question. I am not aware of any such filter... you can set an input callback on the remote IO unit and get a stream of floats.
ReplyDeleteI haven't seen any documentation that this float stream is in some way already processed.
I have written a pitch detector which successfully picks up notes at the low end of my singing range (~80Hz)
Maybe post code -- what are you doing inside this callback?
Can I recommend you re-ask this question, with an appropriate title ( Maybe something like ' how to prevent iPhone from filtering low frequencies ( < 150Hz ) ' ) and containing all of the necessary information, also stick in that picture, that is an important part of the question. it's very easy to embed a picture.
ReplyDeleteyou haven't even stated what it is you are trying to accomplish.
It looks like you're new to the site, and it looks like a very interesting question.
But you are not being at all clear.
Firstly, this is an issue with record or playback or both?
Now, what exactly are you doing in your experiment? Western scientific method... lay it out.
You are generating static in your remoteIO render callback? No, it sounds like you are using passthrough, you say you are putting white noise into the iPhone. how are you setting up your audio unit?
so the white noise goes into the iPhone, straight out, and... now what does that graph represent? FFT / Spectral analysis of the output of the iPhone? how are you doing that? You're feeding this into some OSX graphing software that can take input from the line in?
also, you could do with narrowing it down. If you are indeed passing sound right through the device, you have no idea where the filtering is occurring. for all you know there could be filtering occurring in the device that generates the white noise, as well as the device that graphs the iPhone output.
how about generating your white noise from inside the remote I/O render callback?
and then generating it externally and analysing the data that arrives from within this callback -- you could perform an FFT.
anyway, for input wouldn't you do better to feed in sine waves of different frequencies?