hi im trying to make real time sound effect app I managed to add pitch and reverb adjuster and now I want to control left and right channels tried AVAudioPlayerNode.pan and did not effect the output and tried mainmixernode and didnt work
import UIKit
import AVFoundation
class ViewController: UIViewController {
var audioEngine: AVAudioEngine!
var pitchEffect: AVAudioUnitTimePitch!
var reverbEffect: AVAudioUnitReverb!
var delayEffect: AVAudioUnitDelay!
var audioSession: AVAudioSession!
var playerNode: AVAudioPlayerNode!
var mixerNode: AVAudioMixerNode!
// Additional property for balance
var balanceSlider: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
requestMicrophonePermission()
}
func requestMicrophonePermission() {
audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(.playAndRecord, mode: .default, options: [.defaultToSpeaker, .allowBluetoothA2DP, .allowBluetooth])
try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
audioSession.requestRecordPermission { granted in
DispatchQueue.main.async {
if granted {
print("Microphone access granted")
self.setupAudioEngine()
self.startAudioEngine()
self.setupUI()
} else {
print("Microphone access denied")
}
}
}
} catch {
print("Failed to set up audio session: (error.localizedDescription)")
}
}
func setupAudioEngine() {
audioEngine = AVAudioEngine()
playerNode = AVAudioPlayerNode()
pitchEffect = AVAudioUnitTimePitch()
pitchEffect.pitch = 0 // Default pitch value
reverbEffect = AVAudioUnitReverb()
reverbEffect.loadFactoryPreset(.mediumRoom)
reverbEffect.wetDryMix = 0 // Default reverb value
delayEffect = AVAudioUnitDelay()
delayEffect.delayTime = 0 // Default delay time in seconds
// Create mono format
let monoFormat = AVAudioFormat(standardFormatWithSampleRate: 44100, channels: 1)
mixerNode = audioEngine.mainMixerNode
mixerNode.outputVolume = 0.5 // Default volume
let inputNode = audioEngine.inputNode
let inputFormat = inputNode.inputFormat(forBus: 0)
audioEngine.attach(playerNode)
audioEngine.attach(pitchEffect)
audioEngine.attach(reverbEffect)
audioEngine.attach(delayEffect)
// Connect nodes with mono format
audioEngine.connect(playerNode, to: pitchEffect, format: inputFormat)
audioEngine.connect(pitchEffect, to: reverbEffect, format: inputFormat)
audioEngine.connect(reverbEffect, to: delayEffect, format: inputFormat)
audioEngine.connect(delayEffect, to: mixerNode, format: inputFormat)
audioEngine.connect(mixerNode, to: audioEngine.outputNode, format: monoFormat)
mixerNode.pan = 1
inputNode.installTap(onBus: 0, bufferSize: 1024, format: inputFormat) { [weak self] (buffer, when) in
guard let self = self else { return }
self.processMonoAudioBuffer(buffer)
self.playerNode.scheduleBuffer(buffer, completionHandler: nil)
}
}
func processMonoAudioBuffer(_ buffer: AVAudioPCMBuffer) {
let channels = buffer.format.channelCount
if channels > 1 {
let leftChannel = buffer.floatChannelData?[0]
let rightChannel = buffer.floatChannelData?[1]
if let left = leftChannel, let right = rightChannel {
for i in 0..<Int(buffer.frameLength) {
let average = (left[i] + right[i]) / 2
left[i] = average
right[i] = average
}
}
}
}
func startAudioEngine() {
do {
try audioEngine.start()
playerNode.play()
print("Audio engine started")
} catch {
print("Failed to start audio engine: (error.localizedDescription)")
}
mixerNode.pan = 1
}
func setupUI() {
let pitchSlider = UISlider(frame: CGRect(x: 96, y: 460, width: 200, height: 10))
pitchSlider.minimumValue = -1000 // -2 octaves
pitchSlider.maximumValue = 1000 // +2 octaves
pitchSlider.value = 0 // Default pitch shift
pitchSlider.addTarget(self, action: #selector(pitchSliderChanged(_:)), for: .valueChanged)
view.addSubview(pitchSlider)
let reverbSlider = UISlider(frame: CGRect(x: 96, y: 512, width: 200, height: 10))
reverbSlider.minimumValue = 0 // 0% reverb
reverbSlider.maximumValue = 100 // 100% reverb
reverbSlider.value = 0 // Default reverb
reverbSlider.addTarget(self, action: #selector(reverbSliderChanged(_:)), for: .valueChanged)
view.addSubview(reverbSlider)
let delaySlider = UISlider(frame: CGRect(x: 96, y: 572, width: 200, height: 10))
delaySlider.minimumValue = 0 // 0 seconds
delaySlider.maximumValue = 2 // 2 seconds
delaySlider.value = 0 // Default delay time
delaySlider.addTarget(self, action: #selector(delaySliderChanged(_:)), for: .valueChanged)
view.addSubview(delaySlider)
let volumeSlider = UISlider(frame: CGRect(x: 96, y: 625, width: 200, height: 10))
volumeSlider.minimumValue = 0 // Mute
volumeSlider.maximumValue = 1 // Full volume
volumeSlider.value = 0.5 // Default volume
volumeSlider.addTarget(self, action: #selector(volumeSliderChanged(_:)), for: .valueChanged)
view.addSubview(volumeSlider)
// Add balance slider
balanceSlider = UISlider(frame: CGRect(x: 96, y: 675, width: 200, height: 10))
balanceSlider.minimumValue = -1 // Fully left
balanceSlider.maximumValue = 1 // Fully right
balanceSlider.value = 0 // Center
balanceSlider.addTarget(self, action: #selector(balanceSliderChanged(_:)), for: .valueChanged)
view.addSubview(balanceSlider)
}
@objc func pitchSliderChanged(_ sender: UISlider) {
pitchEffect.pitch = sender.value
}
@objc func reverbSliderChanged(_ sender: UISlider) {
reverbEffect.wetDryMix = sender.value
}
@objc func delaySliderChanged(_ sender: UISlider) {
delayEffect.delayTime = TimeInterval(sender.value)
}
@objc func volumeSliderChanged(_ sender: UISlider) {
mixerNode.outputVolume = sender.value
}
@objc func balanceSliderChanged(_ sender: UISlider) {
let panValue = sender.value
mixerNode.pan = panValue
}
}
tried AVAudioPlayerNode.pan and did not effect the output and tried mainmixernode and didnt wo
New contributor
user26427066 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.