¿Cómo reproducir datos de audio decodificados a través de hardware en IOS?

CorePress2024-01-24  10

Tengo mi propio motor de sonido escrito en C++ y tiene algunas capacidades personalizadas. Tengo los valores PCM decodificados + procesados ​​en mi motor, pero necesito reproducirlos de alguna manera a través de una biblioteca en IOS. Funciona en base a un sistema de devolución de llamada. Un tercero solicita devoluciones de llamada con parámetros determinados como numberOfFrames, pointerToFillData, etc.

Soy muy nuevo en IOS Audio y no sé cómo se realiza este trabajo. En Android, Oboe es la biblioteca que hace esto.

He encontrado algunas bibliotecas personalizadas escritas como novocaína que son muy similares a lo que necesito, pero no son exactamente así. Miré Core Audio pero me parece que Core Audio SDK está en desuso. Sin embargo, Audio Unit que incluía Core Audio parece ser lo que necesito.

Hay varias formas de hacer esto. este yoEs por eso que realmente me vendría bien un ejemplo + sugerencias sobre por qué su manera es la preferible...

¿Alguien puede guiarme en la dirección correcta con ejemplos?

Vi la respuesta de @ hotpaw2 pero necesito un ejemplo con una razón por la cual su respuesta es mejor que otras opciones disponibles.



------------------------------------

La API de iOS Audio Queue (actualmente) no está obsoleta y se basa en devolución de llamadas.

Ver: https://developer.apple.com/documentation/audiotoolbox/audio_queue_services

12

Gracias por responder. Realmente lo aprecio. Aquí tengo algunas preguntas en mente: En este momento, mi motor de audio solo tiene 2 efectos. Podría estar totalmente equivocado aquí, solo una suposición de alguna manera. ¿Puedo usar los efectos integrados de Apple para agregar efectos a mis datos PCM? También he visto un ejemplo de AVAudioEngine AudioUnit con devoluciones de llamada aquí stackoverflow.com/questions/50996398/… ¿Cuáles son las diferencias entre el tuyo y este?

- chico cs

27/03/2021 a las 21:05

¿También tienes algún buen ejemplo para esto? Intenté buscarlo pero todos los ejemplos son de 2012 a 2015

- chico cs

27/03/2021 a las 23:31

@csguy Las colas de audio no han cambiado mucho en los últimos 10 años.

- Gordon Childs

29/03/2021 a las 20:16

@RhythmicFistman Ya veo, ¿por qué deberíamos preferir las colas de audio a las unidades de audio?

- chico cs

29/03/2021 a las 20:26

Utilizo la unidad de audio IO remota para la salida cuando la latencia es importante. El tamaño del búfer de E/S de iOS no está vinculado explícitamente a ninguna API, y tal vez puedas configurar un AQ o AVAudioEngine para hacer lo correcto, pero creo que tus posibilidades son mejores con la AU. @ hotpaw2 ¿estás seguro de que las AudioUnits están en desuso? Pensé que era sólo la API de AUGraph.

- Gordon Childs

29/03/2021 a las 20:52



------------------------------------

Definitivamente necesitas un AVAudioEngine.

let engine = AVAudioEngine()
let player = AVAudioPlayerNode()
var scheduledFrames: AVAudioFrameCount = 0

init() throws {
    engine.attach(player)
    engine.prepare()
    try engine.start()
}

func addBuffer(buffer: AVAudioPCMBuffer) {
    // this will add the buffer to the end of the queue, read more in the docs
    scheduledFrames += buffer.frameLength
    player.scheduleBuffer(buffer, at: nil) {
        // buffer was played here
        scheduledFrames -= buffer.frameLength
        if scheduledFrames < 44100 { // I used around a second here, calculate depending your format
            // request new buffer
        }
    }
}

puedes crear un AVAudioPCMBuffer como este:

var data: UnsafeMutablePointer<UnsafeMutablePointer<Float>>!
// fill your data
let count = 100
guard
    let format = AVAudioFormat(
        standardFormatWithSampleRate: 44100,
        channels: 1
    ),
    let buffer = AVAudioPCMBuffer(
        pcmFormat: format,
        frameCapacity: AVAudioChannelCount(count)
    )
else {
    // throw
    return
}
for channel in 0..<Int(format.channelCount) {
    memcpy(buffer.floatChannelData![channel],
           data[channel],
           count * MemoryLayout<Float>.size)
}

Reemplace floatChannelData por int16ChannelData o int32ChannelData si lo necesita

4

Casi lo que necesito. ¿Cómo puedo programar una reproducción continua? Quiero decir que necesito poder reproducir mis datos continuamente sin espacios, no solo una vez contiene un buffer como aquí

- chico cs

30 de marzo de 2021 a las 12:31

Agregué el inicio de sesión de cálculo de cuadros programados. La devolución de llamada de ScheduleBuffer se llama después de que se reproduce el búfer, así que mantenga alrededor de 1 segundo de búferes en la cola

- Phil Dukhov

30/03/2021 a las 21:02

No es exactamente lo que esperaba, esto parecee una solución alternativa. Este código parece apto para reproducir solo un búfer, ¿me equivoco?

- chico cs

31 de marzo de 2021 a las 0:23

1

No, puedes programar muchos buffers a la vez. Esto es más como un pseudocódigo, debe comenzar a solicitar búferes cuando comience a reproducir y detenerse cuando haya programado lo suficiente, y seguir programando: cuando se llama a la finalización de n búfer, programa el número de búfer n+44100. Por lo tanto, siempre mantienes 1 segundo de búfer programado para reproducirse. Puedes programar todos los buffers a la vez si no te preocupas por el uso de la memoria, también funcionará. Para restablecer los buffers queue necesitas llamar a player.stop()

- Phil Dukhov

31 de marzo de 2021 a las 3:38

Su guía para un futuro mejor - libreflare
Su guía para un futuro mejor - libreflare