Я использую код Apple AVAudioEngine следующим образом:
var audioEngine = AVAudioEngine()
audioEngine.inputNode.installTap { [buffer, when] in
let waveform = myGetWaveform(buffer)
Task {
await Something.shared.doSomethingWith(waveform)
}
}
Где закрытие для installTap будет выполнено в каком-то аудиопотоке.
Я делаю это, потому что Something — это actor, и мне сложно его изменить. Но, конечно, проблема в том, что эти Task могут выполняться не по порядку. Это было бы плохо — мне нужно, чтобы их казнили по порядку. Как я могу гарантировать, что они будут выполняться по порядку? Я полагаю, что это можно применить к любому синхронному коду, который не может изменить вызывающую сторону на асинхронную. Кажется, я не могу найти способ внедрить сериализацию в код асинхронного актера.
Многие версии, которые я нахожу (с использованием семафоров и т. д.), похоже, нарушают асинхронный контракт "потоки должны двигаться вперед"...
РАЗЪЯСНЕНИЕ:
await, извини.buffer будет содержать данные аудиосигнала, взятые с микрофона устройства. Это загадка: в приведенной мной реализации doSomethingWith будет вызываться снова и снова для каждого нового аудиобуфера, но не обязательно будет вызываться по порядку.Основываясь на ответе Роба, я сделал это:
class AudioUploadQueue {
private var continuation: AsyncStream<[Int16]>.Continuation?
nonisolated lazy var stream: AsyncStream<[Int16]> = {
AsyncStream(bufferingPolicy: .bufferingNewest(3)) { (continuation: AsyncStream<[Int16]>.Continuation) -> Void in
self.continuation = continuation
}
}()
func handleWaveformData(_ waveform: [Int16]) {
self.continuation?.yield(waveform)
}
func stop() {
self.continuation?.finish()
}
func monitorForWaveforms() async {
for await waveform in stream {
await AudioUploader.shared.handleAudioWaveform(waveform)
}
}
}
И создал его экземпляр при запуске микрофона и stop() и обнулил его, когда микрофон остановился. Честно говоря, это как-то некрасиво. Я удивлен, что у Swift нет лучшего встроенного ответа. В iOS все еще существуют всевозможные части кода без асинхронной обработки потоков, такие как материал AVAudioEngine, который должен взаимодействовать с остальной частью кода приложения...
Что именно нужно выполнить для того, чтобы?





Это возможно с помощью экспериментальной функции ClosureIsolation, которая скоро должна появиться в Swift 6.
Если вы не можете дождаться версии Swift с этим, то ваш лучший инструмент — AsyncStream. Вы будете отправлять сообщения в поток, а актер будет обрабатывать их в цикле.
Вашему актеру необходимо предоставить свойство yield, содержащее поток, чтобы ваш синхронный код мог получить к нему доступ.
Если вы хотите получить более подробную информацию о предстоящих изменениях (что не обязательно необходимо для этого вопроса), есть ветка Mastodon, в которой Холли Борла из команды Swift ныряет в некоторые из них.
Я не понимаю, а как насчет
awaitперед doSomethingWith(waveform)? Вам нужно выполнить в каком порядке? Я вижу здесь только одну задачу.