Hej. Je souhaite implémenter une application audio en temps réel avec la nouvelle AVAudioEngine
dans Swift. Quelqu'un at-il une expérience avec le nouveau cadre? Comment fonctionnent les applications en temps réel?
Ma première idée était de stocker les données d'entrée (traitées) dans un objet AVAudioPCMBuffer
et de les laisser ensuite jouer avec une AVAudioPlayerNode
comme vous pouvez le voir dans ma classe de démonstration:
import AVFoundation
class AudioIO {
var audioEngine: AVAudioEngine
var audioInputNode : AVAudioInputNode
var audioPlayerNode: AVAudioPlayerNode
var audioMixerNode: AVAudioMixerNode
var audioBuffer: AVAudioPCMBuffer
init(){
audioEngine = AVAudioEngine()
audioPlayerNode = AVAudioPlayerNode()
audioMixerNode = audioEngine.mainMixerNode
let frameLength = UInt32(256)
audioBuffer = AVAudioPCMBuffer(PCMFormat: audioPlayerNode.outputFormatForBus(0), frameCapacity: frameLength)
audioBuffer.frameLength = frameLength
audioInputNode = audioEngine.inputNode
audioInputNode.installTapOnBus(0, bufferSize:frameLength, format: audioInputNode.outputFormatForBus(0), block: {(buffer, time) in
let channels = UnsafeArray(start: buffer.floatChannelData, length: Int(buffer.format.channelCount))
let floats = UnsafeArray(start: channels[0], length: Int(buffer.frameLength))
for var i = 0; i < Int(self.audioBuffer.frameLength); i+=Int(self.audioMixerNode.outputFormatForBus(0).channelCount)
{
// doing my real time stuff
self.audioBuffer.floatChannelData.memory[i] = floats[i];
}
})
// setup audio engine
audioEngine.attachNode(audioPlayerNode)
audioEngine.connect(audioPlayerNode, to: audioMixerNode, format: audioPlayerNode.outputFormatForBus(0))
audioEngine.startAndReturnError(nil)
// play player and buffer
audioPlayerNode.play()
audioPlayerNode.scheduleBuffer(audioBuffer, atTime: nil, options: .Loops, completionHandler: nil)
}
}
Mais c'est loin du temps réel et pas très efficace. Des idées ou des expériences? Et peu importe, si vous préférez Objective-C ou Swift, je vous suis reconnaissant pour toutes les notes, remarques, commentaires, solutions, etc.
J'ai expérimenté AVAudioEngine à la fois en Objective-C et en Swift. Dans la version Objective-C de mon moteur, tout le traitement audio est effectué uniquement en C (en mettant en cache les pointeurs d'échantillon C bruts disponibles via AVAudioPCMBuffer et en opérant sur les données avec uniquement du code C). La performance est impressionnante. Par curiosité, j'ai porté ce moteur à Swift. Avec des tâches telles que la lecture linéaire de fichiers audio ou la génération de sons via la synthèse FM, les performances sont assez bonnes, mais dès que des tableaux sont impliqués (par exemple, avec une synthèse granulaire, où des parties de l’audio sont lues et manipulées de manière non linéaire. ), il y a un impact significatif sur les performances. Même avec la meilleure optimisation, l'utilisation du processeur est 30 à 40% supérieure à celle de la version Objective-C/C. Swift étant nouveau pour moi, il existe peut-être d'autres optimisations que je ne connais pas, mais pour autant que je sache, le C/C++ reste le meilleur choix pour l'audio en temps réel. Regardez aussi The Amazing Audio Engine. J'envisage cela, ainsi que l'utilisation directe de l'ancienne API C.
Si vous devez traiter de l'audio en direct, AVAudioEngine peut ne pas vous convenir. Voir ma réponse à cette question: Je souhaite appeler 20 fois par seconde le fichier installTapOnBus: bufferSize: format: block:
Je pense que cela fait ce que vous voulez: https://github.com/arielelkin/SwiftyAudio
Commentez les distorsions et vous avez une boucle propre.
Apple a officiellement pris position sur le codage en temps réel dans Swift. Lors de la session 2017 de WWDC sur Core Audio, un ingénieur Apple a déclaré ne pas utiliser de méthodes Swift ou Objective C dans le contexte de rappel audio en temps réel (impliquant uniquement l'utilisation de C, ou peut-être de C++ et d'ASM).
Cela implique que le schéma approprié pourrait être d'utiliser Objective C pour votre classe de contrôleur AVAudioEngine, mais uniquement le sous-ensemble C (de Objective C) à l'intérieur du bloc tapOnBus.
Selon Apple lors de la WWDC2017 dans le Quoi de neuf dans Core Audio Video vers 19h20, l’ingénieur dit que l’utilisation de Swift n’était "pas sûre" dans un contexte en temps réel. Posté de la transcription.
Maintenant, comme le rendu du moteur s'effectue à partir d'un contexte En temps réel, vous ne pourrez pas utiliser la méta de rendu Objective-C ou Swift que vous avez vue dans la démonstration.
Et c’est parce qu’il n’est pas sûr d’utiliser Objective-C ou Swift À partir d’un contexte temps réel. Au lieu de cela, le moteur lui-même Vous fournit un bloc de rendu que vous pouvez rechercher et mettre en cache, puis Utilisera ce bloc de rendu ultérieurement pour rendre le moteur à partir du temps réel le contexte. La prochaine chose à faire est de configurer votre nœud d’entrée de manière à ce que Puisse fournir vos données d’entrée au moteur. Et ici, vous Spécifiez le format de l’entrée que vous allez fournir, ce qui peut être Un format différent de celui de la sortie. Et vous fournissez également un bloc que Le moteur appellera chaque fois qu'il aura besoin des données d'entrée. Et lorsque ce bloc Sera appelé, le moteur vous indiquera le nombre de trames d’entrée Dont il a réellement besoin.