Wie implementiert man räumliches Audio mit Swift unter IOS 18?IOS

Programmierung für iOS
Guest
 Wie implementiert man räumliches Audio mit Swift unter IOS 18?

Post by Guest »

Ich habe ein Problem bei der Implementierung räumlicher Audioeffekte in meiner iOS 18-App. Ich habe mehrere Ansätze ausprobiert, um einen 3D-Audioeffekt zu erzielen, aber das Ergebnis fühlte sich nie gut genug an oder es funktionierte überhaupt nicht.
Das Problem, das mich am meisten beunruhigt, ist, dass mir aufgefallen ist, dass ich AirPods habe Ich erkenne meine App nicht als App mit räumlichem Audio. In den Audioeinstellungen wird „Spatial Audio Not Playing“ angezeigt, was mich vermuten lässt, dass meine App kein räumliches Audiopotenzial nutzt.
Es könnte relevant sein zu erwähnen, dass ich ein persönliches Team verwende Konto.
Der erste Ansatz verwendet AVAudioEnviromentNode mit AVAudioEngine. Trotz der Einstellung der Parameter „playerNode.position“ und „listener“ scheinen die räumlichen Effekte nicht zu funktionieren. Das Ändern der listenerPosition oder playerNode.position hat keine spürbaren Auswirkungen auf die Wiedergabe.
Hier ist ein vereinfachtes Beispiel, wie ich AVAudioEngine initialisiere:

Code: Select all

import Foundation
import AVFoundation

class AudioManager: ObservableObject {
// important class variables
var audioEngine: AVAudioEngine!
var environmentNode: AVAudioEnvironmentNode!
var playerNode: AVAudioPlayerNode!
var audioFile: AVAudioFile?
...
//Sound set up
func setupAudio() {
do {
let session = AVAudioSession.sharedInstance()
try session.setCategory(.playback, mode: .default, options: [])
try session.setActive(true)
} catch {
print("Failed to configure AVAudioSession: \(error.localizedDescription)")
}

audioEngine = AVAudioEngine()
environmentNode = AVAudioEnvironmentNode()
playerNode = AVAudioPlayerNode()
audioEngine.attach(environmentNode)
audioEngine.attach(playerNode)
audioEngine.connect(playerNode, to: environmentNode, format: nil)
audioEngine.connect(environmentNode, to: audioEngine.mainMixerNode, format: nil)
environmentNode.listenerPosition = AVAudio3DPoint(x: 0, y: 0, z: 0)
environmentNode.listenerAngularOrientation = AVAudio3DAngularOrientation(yaw: 0, pitch: 0, roll: 0)
environmentNode.distanceAttenuationParameters.referenceDistance = 1.0        environmentNode.distanceAttenuationParameters.maximumDistance = 100.0
environmentNode.distanceAttenuationParameters.rolloffFactor = 2.0
// example.mp3 is mono sound
guard let audioURL = Bundle.main.url(forResource: "example", withExtension: "mp3") else {
print("Audio file not found")
return
}

do {
audioFile = try AVAudioFile(forReading: audioURL)
} catch {
print("Failed to load audio file: \(error)")
}
}
...
//Playing sound
func playSpatialAudio(pan: Float ) {
guard let audioFile = audioFile else { return }
//  left side
playerNode.position = AVAudio3DPoint(x: pan, y: 0, z: 0)
playerNode.scheduleFile(audioFile, at: nil, completionHandler: nil)

do {
try audioEngine.start()
playerNode.play()
} catch {
print("Failed to start audio engine: \(error)")
}
...
}
Der zweite Ansatz ist mit PHASE komplexer – er war besser. Ich habe eine beispielhafte App erstellt, die es Spielern ermöglicht, Audioquellen im 3D-Raum zu verschieben. Ich habe Hall und Schieberegler hinzugefügt, die die Audioposition bis zu 10 Meter in jede Richtung vom Zuhörer ändern, aber der Ton scheint sich wirklich nur von links nach rechts (x-Achse) zu ändern – auch hier denke ich, dass es ein Problem sein könnte, wenn die App nicht als räumlich erkannt wird (AirPods). Einstellungen zeigen immer noch „Spatial Audio Not Playing“).
Hier ist das Beispiel-Setup:

Code: Select all

class PHASEAudioController: ObservableObject{
//Crucial class Variables:
private var soundSourcePosition: simd_float4x4 = matrix_identity_float4x4
private var audioAsset: PHASESoundAsset!
private let phaseEngine: PHASEEngine
private let params = PHASEMixerParameters()
private var soundSource: PHASESource
private var phaseListener:  PHASEListener!
private var soundEventAsset: PHASESoundEventNodeAsset?

// Initialization of PHASE
init{
do {
let session = AVAudioSession.sharedInstance()
try session.setCategory(.playback, mode: .default, options: [])

try session.setActive(true)
} catch {
print("Failed to configure AVAudioSession: \(error.localizedDescription)")
}
// Init PHASE Engine
phaseEngine = PHASEEngine(updateMode: .automatic)
phaseEngine.defaultReverbPreset = .mediumHall
phaseEngine.outputSpatializationMode = .automatic //nothing helps
// Set listener position to (0,0,0) in World space
let origin: simd_float4x4 = matrix_identity_float4x4
phaseListener = PHASEListener(engine: phaseEngine)
phaseListener.transform = origin
phaseListener.automaticHeadTrackingFlags = .orientation
try! self.phaseEngine.rootObject.addChild(self.phaseListener)
do{
try self.phaseEngine.start();
}
catch {
print("Could not start PHASE engine")
}

audioAsset = loadAudioAsset()
// Create sound Source
// Sphere
soundSourcePosition.translate(z:3.0)
let sphere = MDLMesh.newEllipsoid(withRadii: vector_float3(0.1,0.1,0.1), radialSegments: 14, verticalSegments: 14, geometryType: MDLGeometryType.triangles, inwardNormals: false, hemisphere: false, allocator: nil)
let shape = PHASEShape(engine: phaseEngine, mesh: sphere)
soundSource = PHASESource(engine: phaseEngine, shapes: [shape])
soundSource.transform = soundSourcePosition
print(soundSourcePosition)
do {
try phaseEngine.rootObject.addChild(soundSource)
}
catch {
print ("Failed to add a child object to the scene.")
}
let simpleModel = PHASEGeometricSpreadingDistanceModelParameters()
simpleModel.rolloffFactor = rolloffFactor
soundPipeline.distanceModelParameters = simpleModel

let samplerNode = PHASESamplerNodeDefinition(
soundAssetIdentifier: audioAsset.identifier,
mixerDefinition: soundPipeline,
identifier: audioAsset.identifier + "_SamplerNode")
samplerNode.playbackMode = .looping
do {soundEventAsset = try
phaseEngine.assetRegistry.registerSoundEventAsset(
rootNode: samplerNode,
identifier: audioAsset.identifier + "_SoundEventAsset")
} catch {
print("Failed to register a sound event asset.")
soundEventAsset = nil
}
}

//Playing sound
func playSound(){
// Fire new sound event with currently set properties
guard let soundEventAsset else { return }

params.addSpatialMixerParameters(
identifier: soundPipeline.identifier,
source: soundSource,
listener: phaseListener)
let soundEvent = try! PHASESoundEvent(engine: phaseEngine,
assetIdentifier: soundEventAsset.identifier,
mixerParameters: params)
soundEvent.start(completion: nil)
}
...
}
Auch ich habe mit RealityKit experimentiert, aber ich hoffe, eine Lösung ohne die Verwendung der AR-Ansicht zu finden. PHASE scheint die beste Option zu sein, wenn es wie beabsichtigt funktioniert.
Was ich erwarte
Ich hoffe, dass meine App Audio im 3D-Raum in allen Achsen korrekt positioniert, wie in einigen anderen räumlichen Apps, die auf iOS verfügbar sind, und dass die App als App mit aktiviertem räumlichem Audio erkannt wird durch z.B. AirPods.

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post