¿Cómo puedo usar AVAudioPlayer para reproducir audio más rápido * y * más agudo?

Declaración del problema:

Tengo una colección de efectos de sonido en mi aplicación almacenada como files .m4a (formatting AAC, 48 KHz, 16 bits) que quiero reproducir a una variedad de velocidades y tonos, sin tener que generar previamente todas las variantes como separadas files

Aunque la propiedad .rate de un object AVAudioPlayer puede alterar la velocidad de reproducción, siempre mantiene el tono original, que no es lo que quiero. En lugar de eso, simplemente quiero reproducir la muestra de sonido más rápido o más lento y hacer que el tono vaya hacia arriba o hacia abajo para coincidir, como acelerar o ralentizar una grabadora de cinta tradicional reel-to-reel. En otras palabras, necesito alguna forma de alterar esencialmente la frecuencia de muestreo de audio en cantidades como +2 semitonos (12% más rápido), -5 semitonos (33% más lento), +12 semitonos (2x más rápido), etc.

Pregunta:

¿Hay algún modo de search los datos de audio de PCM lineal de un object AVAudioPlayer , aplicar conversión de frecuencia de muestreo utilizando un marco de iOS diferente y rellenar los datos de audio resultantes en un nuevo object AVAudioPlayer , que luego se puede reproducir normalmente?

Posibles vías:

Estaba leyendo en AudioConverterConvertComplexBuffer . En particular, kAudioConverterSampleRateConverterComplexity_Mastering , y kAudioConverterQuality_Max , y AudioConverterFillComplexBuffer() me llamaron la atención. Entonces parece posible con este marco de conversión de audio. ¿Es esta una vía que debo explorar más?

Requisitos:

  1. En realidad, no necesito reproducción para comenzar al instante. Si la conversión de la frecuencia de muestreo incurre en un ligero retraso, está bien. Todas mis muestras son de 4 segundos o less, así que me imagino que cualquier remuestreo sobre la marcha se produciría rápidamente, del order de 1/10 segundo o less. (Sin embargo, más de 1/2 sería demasiado.)

  2. Realmente preferiría no entrar en material pesado como OpenAL o Core Audio si hay una manera más simple de hacerlo usando un marco de conversión provisto por iOS. Sin embargo, si hay una solución simple a este problema usando OpenAL o Core Audio, me complacería considerar eso. Por "simple" me refiero a algo que se puede implementar en 50-100 líneas de código y no requiere iniciar subprocesss adicionales para alimentar datos a un dispositivo de sonido. Prefiero tener todo cuidado automáticamente, por lo que estoy dispuesto a convertir el clip de audio antes de jugar.

  3. Quiero evitar las bibliotecas de terceros aquí, porque esto no es ciencia espacial y sé que debe ser posible con frameworks nativos de iOS de alguna manera.

  4. De nuevo, necesito ajustar el tono y la velocidad de reproducción juntos, no por separado . Entonces, si la reproducción se ralentiza 2 veces, una voz humana se haría muy profunda y lenta. Y si la reproducción se acelera 2-3 veces, una voz humana parecería una ardilla rápida. En otras palabras, no quiero alterar el tono al mismo time, manteniendo la duración del audio igual, ya que esa operación da como resultado un sonido indeseablemente "metálico" al doblar el tono hacia arriba más de un par de semitonos. Solo quiero acelerar todo y hacer que el tono aumente como un efecto secundario natural, como solían hacer las grabadoras anticuadas.

  5. Necesita funcionar en iOS 6 y versiones superiores, aunque el soporte de iOS 5 sería una buena bonificación.

El enlace del foro que menciona Jack Wu tiene una sugerencia, que implica anular directamente los datos del encabezado AIFF. Esto puede funcionar, pero necesitará tener files AIFF ya que se basa en un range específico del encabezado AIFF para escribir. Esto también debe hacerse antes de crear AVAudioPlayer, lo que significa que no puede modificar el tono una vez que se está ejecutando.

Si está dispuesto a ir a la ruta AudioUnits, una solución simple completa es probablemente ~ 200 líneas (tenga en count que esto supone que el estilo de código que tiene una function toma hasta 7 líneas con un parámetro en cada línea). Hay una AudioVision de Varispeed, que hace exactamente lo que desea al bloquear pitch para calificar. Básicamente necesitará ver la API, documentos y algunos ejemplos de código AudioUnit para familiarizarse y luego:

  1. crear / iniciar el gráfico de audio y el formatting de transmisión (~ 100 líneas)
  2. cree y agregue en el gráfico una unidad de Audio RemoteIO ( kAudioUnitSubType_RemoteIO ) (esto kAudioUnitSubType_RemoteIO al altavoz)
  3. cree y agregue una unidad de kAudioUnitSubType_Varispeed y conecte la salida de la unidad de kAudioUnitSubType_Varispeed ( kAudioUnitSubType_Varispeed ) a la input de la unidad RemoteIO
  4. cree y agregue al gráfico una unidad AudioFilePlayer ( kAudioUnitSubType_AudioFilePlayer ) para leer el file y conéctelo a la unidad de kAudioUnitSubType_AudioFilePlayer
  5. inicia el gráfico para comenzar la reproducción
  6. cuando quiera cambiar el tono, hágalo a través de AudioUnitSetParameter, y el cambio de tono y la velocidad de reproducción tendrá efecto mientras se reproduce

Tenga en count que hay una unidad de audio TimePitch que permite controlar de forma independiente el tono y la velocidad.

Para iOS 7, querría ver el algorithm de paso de time de audioTimePitchAlgorithm ( audioTimePitchAlgorithm ) llamado AVAudioTimePitchAlgorithmVarispeed . Lamentablemente, esta característica no está disponible en los primeros sistemas.