2

I want to play stereo sounds using Audiounit using AUGraph in ios but the problem that I am facing is "My sound is playing fast when i want to play in stereo mode on 48000 samplings rate".But it works fine for mono sounds(Single channel).

Here is my code.

import Combine
import AudioUnit
import Foundation
import AudioToolbox
import AVFoundation

@objc protocol AudioUnitDataChannelPlayoutDelegate
{
    func performInput(
        _ ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>,
        inTimeStamp: UnsafePointer<AudioTimeStamp>,
        inBufNumber: UInt32,
        inNumberFrames: UInt32,
        ioData: UnsafeMutablePointer<AudioBufferList> ) -> OSStatus
}

private let AudioController_InputCallback: AURenderCallback =
{
    (
        inRefCon,
        ioActionFlags,
        inTimeStamp,
        inBufNumber,
        inNumberFrames,
        ioData
    )-> OSStatus in
    
    let delegate = unsafeBitCast(inRefCon, to: AudioUnitDataChannelPlayoutDelegate.self)
    
    let result = delegate.performInput(ioActionFlags,
                                       inTimeStamp: inTimeStamp,
                                       inBufNumber: inBufNumber,
                                       inNumberFrames: inNumberFrames,
                                       ioData: ioData!)
    return noErr
}

class AudioUnitDataChannelPlayout
{
    
    private static var instance: AudioUnitDataChannelPlayout?
    
    public class var sharedInstance: AudioUnitDataChannelPlayout
    {
        if instance == nil
        {
            instance = AudioUnitDataChannelPlayout()
        }
        return instance!
    }
    
    static let audioBuffer = CircularBuffer(size: 25003904)
   
    let kInputBus: UInt32 = 1
    let kOutputBus: UInt32 = 0
    var status: OSStatus?
    var flag: UInt32 = 1
    
    var ioFormat = CAStreamBasicDescription(
        sampleRate: Double(48000.0),
        numChannels: 2,
        pcmf: .int16,
        isInterleaved: true )
    
    var timePitchFormat = CAStreamBasicDescription(
        sampleRate: Double(48000.0),
        numChannels: 1,  // if chnges to 2 nothing happens
        pcmf: .float32,
        isInterleaved: false ) // if change to true then error occurs
    
    var graph: AUGraph?
    var firstConverterNode: AUNode = 0
    var timePitchNode: AUNode = 0
    var secondConverterNode: AUNode = 0
    var outputNode: AUNode = 0
    var firstConverterUnit: AudioUnit?
    var timePitchUnit: AudioUnit?
    var secondConverterUnit: AudioUnit?
    var outputUnit: AudioUnit?
    
    
    init()
    {
        configureAudioSession()
        setupRecordingUnit()
        playAudio()
    }
    
    private func setupRecordingUnit()
    {
        check(error: NewAUGraph(&graph), description: "Failed to create AUGraph")
        
        var output_desc = AudioComponentDescription(
            componentType: OSType(kAudioUnitType_Output),
            componentSubType: OSType(kAudioUnitSubType_VoiceProcessingIO),
            componentManufacturer: OSType(kAudioUnitManufacturer_Apple),
            componentFlags: 0,
            componentFlagsMask: 0 )
        
        var converter_desc = AudioComponentDescription(
            componentType: OSType(kAudioUnitType_FormatConverter),
            componentSubType: OSType(kAudioUnitSubType_AUConverter),
            componentManufacturer: OSType(kAudioUnitManufacturer_Apple),
            componentFlags: 0,
            componentFlagsMask: 0 )
        
        var varispeed_desc = AudioComponentDescription(
            componentType: OSType(kAudioUnitType_FormatConverter),
            componentSubType: OSType(kAudioUnitSubType_NewTimePitch),
            componentManufacturer: OSType(kAudioUnitManufacturer_Apple),
            componentFlags: 0,
            componentFlagsMask: 0 )
        
        check(error: AUGraphAddNode(graph!, &output_desc, &outputNode), description: "Failed to Add Node Audio Node")
        
        check(error: AUGraphAddNode(graph!, &converter_desc, &firstConverterNode), description: "Failed to Add converter node")
        check(error: AUGraphAddNode(graph!, &varispeed_desc, &timePitchNode), description: "Failed to Add Node varispeed Node")
        check(error: AUGraphAddNode(graph!, &converter_desc, &secondConverterNode), description: "Failed to Add converter node")
        
        check(error: AUGraphOpen(graph!), description: "Failed to open AUGraph")
        
        check(error: AUGraphNodeInfo(graph!, outputNode, nil, &outputUnit), description: "Failed to get Info of audioUnit")
        check(error: AUGraphNodeInfo(graph!, firstConverterNode, nil, &firstConverterUnit), description: "Failed to get Info of converterUnit")
        check(error: AUGraphNodeInfo(graph!, timePitchNode, nil, &timePitchUnit), description: "Failed to get Info of VarispeedUnit")
        check(error: AUGraphNodeInfo(graph!, secondConverterNode, nil, &secondConverterUnit), description: "Failed to get Info of converterUnit")
        
        check(error: AudioUnitSetProperty(outputUnit!,kAudioOutputUnitProperty_EnableIO,kAudioUnitScope_Output,kOutputBus,&flag,MemoryLayoutStride.SizeOf32(flag)),
              description: "Failed to set enable IO for Playing.")
        
        check(error: AudioUnitSetProperty(firstConverterUnit!, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &ioFormat, MemoryLayoutStride.SizeOf32(ioFormat)), description: "Failed to set property of firstConverter Unit")
        
        check(error: AudioUnitSetProperty(firstConverterUnit!, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kOutputBus, &timePitchFormat, MemoryLayoutStride.SizeOf32(timePitchFormat)), description: "Failed to set property of firstConverter Unit 1")
        
        check(error: AudioUnitSetProperty(timePitchUnit!, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &timePitchFormat, MemoryLayoutStride.SizeOf32(timePitchFormat)), description: "Failed to set property of varispeed unit")
        
        check(error: AudioUnitSetProperty(timePitchUnit!, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kOutputBus, &timePitchFormat, MemoryLayoutStride.SizeOf32(timePitchFormat)), description: "Failed to set property of Varispeed Unit 1")
        
        check(error: AudioUnitSetProperty(secondConverterUnit!, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &timePitchFormat, MemoryLayoutStride.SizeOf32(timePitchFormat)), description: "Failed to set property of firstConverter Unit")
        
        check(error: AudioUnitSetProperty(secondConverterUnit!, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kOutputBus, &ioFormat, MemoryLayoutStride.SizeOf32(ioFormat)), description: "Failed to set property of firstConverter Unit 1")
        
        
        check(error: AudioUnitSetProperty(outputUnit!, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &ioFormat, MemoryLayoutStride.SizeOf32(ioFormat)), description: "Failed to set property of Output Unit")
        AUGraphConnectNodeInput(graph!, firstConverterNode, 0, timePitchNode, 0)
        AUGraphConnectNodeInput(graph!, timePitchNode, 0, secondConverterNode, 0)
        AUGraphConnectNodeInput(graph!, secondConverterNode, 0, outputNode, 0)
        
        
        var recordingCallback = AURenderCallbackStruct(
            inputProc: AudioController_InputCallback,
            inputProcRefCon: UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) )
        
        check(error: AUGraphSetNodeInputCallback(graph!, firstConverterNode, 0, &recordingCallback), description: "Failed to set inPutCallback")
        
        AUGraphInitialize(graph!)
    }
    
    func check(error: OSStatus, description: String)
    {
        if error != noErr
        {
            fatalError("\(description) : \(error)")
        }
    }
    
    func startSpeakerUnit()
    {
        check(error: AUGraphStart(graph!), description: "Error Failed to start AUGraph")
        print("initializing the speaker unit")
    }
    
    func stopSpeakerUnit()
    {
        check(error: AUGraphStop(graph!), description: "Error Failed to stop AUGraph")
        AudioUnitDataChannelPlayout.instance = nil
    }

    private func playAudio()
    {
        AudioUnitSetParameter(self.timePitchUnit!, kNewTimePitchParam_Rate, kAudioUnitScope_Global,0, AudioUnitParameterValue(1.0), 0)
    }
    
}

extension AudioUnitDataChannelPlayout: AudioUnitDataChannelPlayoutDelegate
{
    func performInput(_ ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>, inTimeStamp: UnsafePointer<AudioTimeStamp>, inBufNumber: UInt32, inNumberFrames: UInt32, ioData: UnsafeMutablePointer<AudioBufferList>) -> OSStatus
    {
        
        let playbackPointer = ioData[0].mBuffers
        
        print("SECOND POINTER: \(playbackPointer)")

        let bytesToCopy = ioData[0].mBuffers.mDataByteSize

        
        var bufferTail = AudioUnitDataChannelPlayout.audioBuffer.getTail()
        
        print(AudioUnitDataChannelPlayout.audioBuffer.getAvailableBytes())

        let bytesToWrite = min(bytesToCopy, AudioUnitDataChannelPlayout.audioBuffer.getAvailableBytes())
        
        memcpy(playbackPointer.mData, bufferTail, Int(bytesToWrite))
        
        print("playing...\(bytesToCopy)")
        AudioUnitDataChannelPlayout.audioBuffer.consume(samples: bytesToWrite)
        
        return noErr
    }
    
}

In this class, I am just playing wave files of stereo sounds. Any help in this regard will be highly appreciated.

  • Is it really playing faster? Or is it pitch shifted? Or both? Does it happen with all stereo sources? Please give more details about the issue so we can maybe guess what can be the cause. – Eric Aya Jul 07 '21 at 08:06
  • Yes, it happens with all stereo sources and it is also playing very fast and I am not sure about either its pitch is shifted or not. Here is the link of both wave file before and after passing from augraph https://drive.google.com/drive/folders/1-t7exb5FQ50-f32AT9Y5Y7R5wS4rIJxq?usp=sharing Last but not least is Thanks a lot for your concern. – Muhammed Shehryar Sherazi Jul 07 '21 at 08:36
  • I don't know about your code, but to the ear, it sounds like a 2x speed effect has been applied, without correcting the pitch shift that this creates. Either you have this effect in your stereo chain without knowing it, or you have an issue probably like mismatched values between input and output. – Eric Aya Jul 07 '21 at 08:50
  • I see there's a `timePitchNode` in your code. This could be the origin of the problem (I would start debugging around this anyway, before doing anything else). For example, deactivate this effect entirely and see if your export is still faulty. – Eric Aya Jul 07 '21 at 08:54
  • For the first part, I have gone through all code and try to find that either speed effect has been applied or not but I didn't get it anywhere. And i am just giving the raw .wav file in player and i think its input and output are the same. For the second part , timepitchNode is working fine with mono sounds but I am going to try as you said. – Muhammed Shehryar Sherazi Jul 07 '21 at 09:20
  • Can you please guide me about how we can control our 2x speed or where it will be in the stereo chain and how the pitch is shifted. Or if you have already played stereo sounds by any method then can you tell me about that? – Muhammed Shehryar Sherazi Jul 07 '21 at 09:25

1 Answers1

2

There is no mistake in my Player code that is written up there. But the main reason for playing audio in fast mode was that while inserting my data into the player, I was giving stereo data but treated it as mono(As I was filling only half the number of frames). I multiplied the number of frames with the number of channels while allocating the memory and it did the trick.