我想在 ios 中使用 AUGraph 使用 Audiounit 播放立体声,但我面临的问题是“当我想以 48000 采样率在立体声模式下播放时,我的声音播放速度很快”。但它适用于单声道声音(单声道)。
这是我的代码。
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
}
}
在这堂课中,我只是在播放立体声的波形文件。在这方面的任何帮助将不胜感激。