0

I am trying to take audio from the mic in and apply a 180 phase shift to that input stream and output it.

Here is the code I'm using to init the session and capture the audio (sample rate is set to 44.1 KHz)

OSStatus status = noErr;

status = AudioSessionSetActive(true);
assert(status == noErr);

UInt32 category = kAudioSessionCategory_PlayAndRecord;
status = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(UInt32), &category);
assert(status == noErr);

float aBufferLength = 0.002902; // In seconds


status = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration,
                                 sizeof(aBufferLength), &aBufferLength);

assert(status == noErr);

AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;

// get AU component
AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc);

// create audio unit by component
status = AudioComponentInstanceNew(inputComponent, &_audioState->audioUnit);
assert(status == noErr);

// record io on the input bus
UInt32 flag = 1;
status = AudioUnitSetProperty(_audioState->audioUnit,
                              kAudioOutputUnitProperty_EnableIO,
                              kAudioUnitScope_Input,
                              1, /*input*/
                              &flag,
                              sizeof(flag));
assert(status == noErr);

// play on io on the output bus
status = AudioUnitSetProperty(_audioState->audioUnit,
                              kAudioOutputUnitProperty_EnableIO,
                              kAudioUnitScope_Output,
                              0, /*output*/
                              &flag,
                              sizeof(flag));

assert(status == noErr);


// Fetch sample rate, in case we didn't get quite what we requested
Float64 achievedSampleRate;
UInt32 size = sizeof(achievedSampleRate);
status = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate, &size, &achievedSampleRate);
if ( achievedSampleRate != SAMPLE_RATE ) {
    NSLog(@"Hardware sample rate is %f", achievedSampleRate);
} else {
    achievedSampleRate = SAMPLE_RATE;
    NSLog(@"Hardware sample rate is %f", achievedSampleRate);
}


// specify stream format for recording
AudioStreamBasicDescription audioFormat;
audioFormat.mSampleRate = achievedSampleRate;
audioFormat.mFormatID = kAudioFormatLinearPCM;
audioFormat.mFormatFlags = kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
audioFormat.mFramesPerPacket = 1;
audioFormat.mChannelsPerFrame = 1;
audioFormat.mBitsPerChannel = 16;
audioFormat.mBytesPerPacket = 2;
audioFormat.mBytesPerFrame = 2;

// set the format on the output stream
status = AudioUnitSetProperty(_audioState->audioUnit,
                              kAudioUnitProperty_StreamFormat,
                              kAudioUnitScope_Output,
                              kInputBus,
                              &audioFormat,
                              sizeof(audioFormat));

assert(status == noErr);

// set the format on the input stream
status = AudioUnitSetProperty(_audioState->audioUnit,
                              kAudioUnitProperty_StreamFormat,
                              kAudioUnitScope_Input,
                              kOutputBus,
                              &audioFormat,
                              sizeof(audioFormat));
assert(status == noErr);

AURenderCallbackStruct callbackStruct;
memset(&callbackStruct, 0, sizeof(AURenderCallbackStruct));
callbackStruct.inputProc = RenderCallback;
callbackStruct.inputProcRefCon = _audioState;

// set input callback
status = AudioUnitSetProperty(_audioState->audioUnit,
                              kAudioOutputUnitProperty_SetInputCallback,
                              kAudioUnitScope_Global,
                              kInputBus,
                              &callbackStruct,
                              sizeof(callbackStruct));
assert(status == noErr);

callbackStruct.inputProc = PlaybackCallback;
callbackStruct.inputProcRefCon = _audioState;

// set Render callback for output
status = AudioUnitSetProperty(_audioState->audioUnit,
                              kAudioUnitProperty_SetRenderCallback,
                              kAudioUnitScope_Global,
                              kOutputBus,
                              &callbackStruct,
                              sizeof(callbackStruct));
assert(status == noErr);

flag = 0;

// allocate render buffer
status = AudioUnitSetProperty(_audioState->audioUnit,
                              kAudioUnitProperty_ShouldAllocateBuffer,
                              kAudioUnitScope_Output,
                              kInputBus,
                              &flag,
                              sizeof(flag));
assert(status == noErr);

_audioState->audioBuffer.mNumberChannels = 1;
_audioState->audioBuffer.mDataByteSize = 256 * 2;
_audioState->audioBuffer.mData = malloc(256 * 2);

// initialize the audio unit
status = AudioUnitInitialize(_audioState->audioUnit);
assert(status == noErr);
}

Does anyone know a way to shift the phase to create a destructive sine wave? I've heard talk of using vDSP to do band pass filtering but i'm unsure...

patrickjquinn
  • 288
  • 1
  • 15
  • 4
    180 degrees is simple. You just need to flip the sign of your input signal - so a sample of value x becomes -x and visa versa. – jaket Oct 12 '15 at 18:17
  • Your question title asks how to do a 180 degree phase shift (aka invert). Your last sentence says you want to shift the phase and create a destructive sine wave. You could find the fundamental frequency, then find the peaks and sum it with an inverted sine wave but it simply sound like a pure tone mixed in because your fundamental frequency will not be sinusoidal. You should clarify by saying what you are trying to do. Are you trying to eliminate feedback, cancel voices, cancel background noise, etc?? What are you trying to do because your approach is odd? – jaybers Oct 13 '15 at 03:07
  • Active noise cancellation on the sampled band. Obviously I'm not exactly a dab hand with core audio / audio unit stuff and with a background in comp science as apposed to electrical engineering I'm really feeling around in the dark for a light switch. – patrickjquinn Oct 13 '15 at 08:36

1 Answers1

2

Unless you know the delay from the microphone to the input buffer, the delay from the output buffer to the speaker, the frequencies you want to cancel, and some knowledge that these frequencies are stationary over that time, you can't reliably create a 180 degree phase shift for cancellation purposes. Instead you will be trying to cancel the sound that occurred a dozen or more milliseconds earlier, and if that frequency has changed in the meantime, you could end up adding to the sound instead of canceling it. Also if the distance between the sound source, the speaker source and the listener changes by a large enough fraction of a wavelength, the speaker output could end up additively doubling the loudness of the sound source instead of canceling it. For a 1 kHz sound, that's a 6 inch movement.

Active noise cancellation requires very accurate knowledge of the in-to-out time lags; including the microphone, input filter and speaker responses, and ADC/DAC latencies. Apple does not specify those, and they very likely differ between iOS device models.

Given precise knowledge of the in-to-out latencies, and an accurate analysis of the source signal frequencies (via FFTs), some phase shifts other than 180 degrees at each frequency may be required for an attempt at canceling a stationary source.

hotpaw2
  • 70,107
  • 14
  • 90
  • 153