0

We are working with Audio Unit on iOS as part of a VOIP application. We have successfully played audio but now we would like to control the playing speed and pitch of audio in real time. We are getting real time audio bytes from the UDP socket.

This is the code for audio unit init for playing:

init(_ client: UDPClient, _ tcpClient: TCPClient, _ opusHelper: OpusHelper, _ tvTemp: UILabel) {
        super.init()
        let success = initCircularBuffer(&circularBuffer, 4096)
        if success {
            print("Circular buffer init was successful")
        } else {
            print("Circular buffer init not successful")
        }

        self.tvTemp = tvTemp
        self.opusHelper = opusHelper
        monotonicTimer = MonotonicTimer()

        udpClient = client
        self.tcpClient = tcpClient

        var desc = AudioComponentDescription(
            componentType: OSType(kAudioUnitType_Output),
            componentSubType: OSType(kAudioUnitSubType_VoiceProcessingIO),
            componentManufacturer: OSType(kAudioUnitManufacturer_Apple),
            componentFlags: 0,
            componentFlagsMask: 0
        )

        let inputComponent = AudioComponentFindNext(nil, &desc)

        status = AudioComponentInstanceNew(inputComponent!, &audioUnit)
        if status != noErr {
            print("Audio component instance new error \(status!)")
        }

        // Enable IO for recording
        var flag: UInt32 = 1

        // Enable IO for playback
        status = AudioUnitSetProperty(
            audioUnit!,
            kAudioOutputUnitProperty_EnableIO,
            kAudioUnitScope_Output,
            kOutputBus,
            &flag,
            MemoryLayoutStride.SizeOf32(flag)
        )
        if status != noErr {
            print("Enable IO for playback error \(status!)")
        }



        var ioFormat = CAStreamBasicDescription(
            sampleRate: 48000.0,
            numChannels: 1,
            pcmf: .int16,
            isInterleaved: false
        )



        status = AudioUnitSetProperty(
            audioUnit!,
            AudioUnitPropertyID(kAudioUnitProperty_StreamFormat),
            AudioUnitScope(kAudioUnitScope_Input),
            0,
            &ioFormat!,
            MemoryLayoutStride.SizeOf32(ioFormat)
        )
        if status != noErr {
            print("Unable to set stream format input to output \(status!)")
        }


        var playbackCallback = AURenderCallbackStruct(
            inputProc: AudioController_PlaybackCallback,
            inputProcRefCon: UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
        )


        status = AudioUnitSetProperty(
            audioUnit!,
            AudioUnitPropertyID(kAudioUnitProperty_SetRenderCallback),
            AudioUnitScope(kAudioUnitScope_Input),
            kOutputBus,
            &playbackCallback,
            MemoryLayout<AURenderCallbackStruct>.size.ui
        )


        if status != noErr {
            print("Failed to set recording render callback \(status!)")
        }

        status = AudioUnitInitialize(audioUnit!)
        if status != noErr {
            print("Failed to initialize audio unit \(status!)")
        }
    }

We are putting audio data from UDP in TPCircular Buffer:

   let decodedData = self.opusHelper?.decodeStream(of: self.jitterGet.buffer)
let _ = TPCircularBufferProduceBytes(&self.circularBuffer, decodedData, UInt32(decodedData!.count * 2))

This is how we are are playing the audio.

func performPlayback(
        _ ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>,
        inTimeStamp: UnsafePointer<AudioTimeStamp>,
        inBufNumber: UInt32,
        inNumberFrames: UInt32,
        ioData: UnsafeMutablePointer<AudioBufferList>
    ) -> OSStatus {
        let buffer = ioData[0].mBuffers


        let bytesToCopy = ioData[0].mBuffers.mDataByteSize
        var bufferTail: UnsafeMutableRawPointer?
        //        print("BYTES TO COPY: \(bytesToCopy)")
        self.availableBytes = 0
        bufferTail = TPCircularBufferTail(&self.circularBuffer, &self.availableBytes)
        bytesToWrite = min(bytesToCopy, self.availableBytes)




        print("BYTES TO WRITE: \(bytesToWrite)")
        if bytesToWrite >= 3840 {
            memcpy(buffer.mData, bufferTail, Int(bytesToWrite))
            TPCircularBufferConsume(&self.circularBuffer, bytesToWrite)
        } else {
            let silence = [Int16](repeating: 0, count: Int(bytesToCopy))
            memcpy(buffer.mData, silence, Int(bytesToCopy))
        }


        return noErr
    }

Now we want to change the SPEED and PITCH of the playing audio, can you please guide us on how to integrate Varispeed and Timepitch in our current configuration, as we found out that these properties may help us.

https://stackoverflow.com/a/59061396/12020007 @hotpaw2 Your answer pointed us to the right path. Now we are looking to change speed and pitch.

shayan
  • 25
  • 6
  • If you know how to connect audio units, just use https://developer.apple.com/documentation/avfoundation/avaudiounittimepitch – hotpaw2 Mar 03 '20 at 18:32
  • Does this mean I need to shift to an AUGraph because right now I'm not using a graph. – shayan Mar 04 '20 at 10:09

0 Answers0