Вызвать Swift Actor из кода AVAudioEngine (синхронно)

Я использую код 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 могут выполняться не по порядку. Это было бы плохо — мне нужно, чтобы их казнили по порядку. Как я могу гарантировать, что они будут выполняться по порядку? Я полагаю, что это можно применить к любому синхронному коду, который не может изменить вызывающую сторону на асинхронную. Кажется, я не могу найти способ внедрить сериализацию в код асинхронного актера.

Многие версии, которые я нахожу (с использованием семафоров и т. д.), похоже, нарушают асинхронный контракт "потоки должны двигаться вперед"...

РАЗЪЯСНЕНИЕ:

  1. Я забыл await, извини.
  2. Когда вы вызываете installTap на inputNode, вы передаете закрытие обратного вызова. Затем, когда inputNode запускается, он будет вызывать данное замыкание снова и снова, много раз в секунду. 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, который должен взаимодействовать с остальной частью кода приложения...

Я не понимаю, а как насчет await перед doSomethingWith(waveform)? Вам нужно выполнить в каком порядке? Я вижу здесь только одну задачу.

sonle 24.06.2024 06:01

Что именно нужно выполнить для того, чтобы?

Sweeper 24.06.2024 06:42
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
63
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это возможно с помощью экспериментальной функции ClosureIsolation, которая скоро должна появиться в Swift 6.

Если вы не можете дождаться версии Swift с этим, то ваш лучший инструмент — AsyncStream. Вы будете отправлять сообщения в поток, а актер будет обрабатывать их в цикле.

Вашему актеру необходимо предоставить свойство yield, содержащее поток, чтобы ваш синхронный код мог получить к нему доступ.

Если вы хотите получить более подробную информацию о предстоящих изменениях (что не обязательно необходимо для этого вопроса), есть ветка Mastodon, в которой Холли Борла из команды Swift ныряет в некоторые из них.

Другие вопросы по теме