Wie kann ich AVAudioPlayer verwenden, um Audio schneller * und * höher zu spielen?

8

Problembeschreibung:

Ich habe eine Sammlung von Soundeffekten in meiner App gespeichert als .m4a -Dateien (AAC-Format, 48 KHz, 16-Bit), die ich mit verschiedenen Geschwindigkeiten und Tonhöhen spielen möchte, ohne alle vorgenerieren zu müssen die Varianten als separate Dateien.

Obwohl die .rate -Eigenschaft eines AVAudioPlayer -Objekts die Wiedergabegeschwindigkeit ändern kann, behält sie immer die ursprüngliche Tonhöhe bei, was nicht das ist, was ich möchte. Stattdessen möchte ich einfach das Soundsample schneller oder langsamer spielen und die Tonhöhe nach oben oder unten anpassen - genau wie das Beschleunigen oder Verlangsamen eines altmodischen Reel-to-Tape-Recorders. Mit anderen Worten, ich brauche eine Möglichkeit, um die Audio-Abtastrate um Größen wie +2 Halbtöne (12% schneller), -5 Halbtöne (33% langsamer), +12 Halbtöne (2x schneller), etc.

zu ändern

Frage:

Gibt es eine Möglichkeit, die linearen PCM-Audiodaten von einem AVAudioPlayer -Objekt abzurufen, die Abtastratenkonvertierung mit einem anderen iOS-Framework durchzuführen und die resultierenden Audiodaten in ein neues AVAudioPlayer -Objekt zu stopfen, das dann normal abgespielt werden kann ?

Mögliche Wege:

Ich habe auf AudioConverterConvertComplexBuffer nachgelesen. Insbesondere kAudioConverterSampleRateConverterComplexity_Mastering , und kAudioConverterQuality_Max , und AudioConverterFillComplexBuffer() sind mir aufgefallen. So sieht es mit diesem Audio-Konvertierungs-Framework möglich aus. Ist das ein Weg, den ich weiter erkunden sollte?

Anforderungen:

  1. Ich brauche eigentlich keine sofortige Wiedergabe. Wenn die Abtastratenumwandlung eine geringfügige Verzögerung verursacht, ist das in Ordnung. Alle meine Samples sind 4 Sekunden oder weniger, also würde ich mir vorstellen, dass eine schnelle Neuabtastung schnell in der Größenordnung von 1/10 Sekunde oder weniger erfolgen würde. (Mehr als 1/2 wäre jedoch zu viel.)

  2. Ich würde lieber nicht in Sachen Schwergewicht wie OpenAL oder Core Audio kommen, wenn es eine einfachere Möglichkeit gibt, dies mit einem Konvertierungs-Framework von iOS zu tun. Wenn es jedoch eine einfache Lösung für dieses Problem mit OpenAL oder Core Audio gibt, würde ich das gerne berücksichtigen. Mit "einfach" meine ich etwas, das in 50-100 Zeilen Code implementiert werden kann und keine zusätzlichen Threads benötigt, um Daten an ein Soundgerät zu senden. Ich hätte lieber alles automatisch erledigt - deshalb bin ich bereit, den Audioclip vor dem Abspielen zu konvertieren.

  3. Ich möchte hier Bibliotheken von Drittanbietern vermeiden, weil das keine Hexenwerk ist und ich weiß, dass es irgendwie mit nativen iOS-Frameworks möglich sein muss.

  4. Auch hier muss ich die Tonhöhe und die Wiedergaberate zusammen einstellen, nicht getrennt . Wenn also die Wiedergabe 2x verlangsamt wird, wird eine menschliche Stimme sehr tief und langsam gesprochen. Und wenn die Wiedergabe 2-3x beschleunigt wird, klingt eine menschliche Stimme wie ein schnell sprechender Streifenhörnchen. Mit anderen Worten, ich möchte die Tonhöhe absolut nicht verändern, während die Audiodauer gleich bleibt, weil diese Operation zu einem unerwünscht "blechern" Klang führt, wenn die Tonhöhe um mehr als ein paar Halbtöne nach oben gebogen wird. Ich möchte nur das Ganze beschleunigen und die Tonhöhe als natürliche Nebenwirkung hochgehen lassen, genau wie altmodische Tonbandgeräte.

  5. Muss in iOS 6 und höher funktionieren, obwohl iOS 5-Unterstützung ein netter Bonus wäre.

Todd Lehman 16.04.2014, 08:11
quelle

2 Antworten

2

Der Forumslink, den Jack Wu erwähnt, hat einen Vorschlag, der das direkte Überschreiben der AIFF-Kopfdaten beinhaltet. Dies funktioniert möglicherweise, aber Sie müssen AIFF-Dateien haben, da es auf einen bestimmten Bereich des AIFF-Headers angewiesen ist, in den geschrieben werden soll. Dies muss auch vor dem Erstellen des AVAudioPlayers erfolgen. Das bedeutet, dass Sie die Tonhöhe nicht mehr ändern können, wenn sie gerade ausgeführt wird.

Wenn Sie bereit sind, zur AudioUnits-Route zu gehen, ist eine komplette einfache Lösung wahrscheinlich ~ 200 Zeilen (beachten Sie, dass dies davon ausgeht, dass der Codestil mit einer Funktion bis zu 7 Zeilen mit einem Parameter in jeder Zeile aufnehmen kann). Es gibt eine Varispeed AudioUnit, die genau das macht, was Sie wollen, indem Sie die Tonhöhe für die Bewertung abstimmen. Sie müssten sich im Grunde die API, die Dokumentation und einen AudioUnit-Beispielcode ansehen, um sich vertraut zu machen und dann:

  1. Erstellen / Init das Audio-Diagramm und Streaming-Format (~ 100 Zeilen)
  2. Erstellen und fügen Sie dem Diagramm eine RemoteIO AudioUnit ( kAudioUnitSubType_RemoteIO ) hinzu (dies wird an den Sprecher ausgegeben)
  3. Erstellen und fügen Sie eine Varispeed-Einheit hinzu und verbinden Sie den Ausgang der Varispeed-Einheit ( kAudioUnitSubType_Varispeed ) mit dem Eingang der RemoteIO-Einheit
  4. Erstellen und fügen Sie dem Graphen eine AudioFilePlayer ( kAudioUnitSubType_AudioFilePlayer ) -Einheit hinzu, um die Datei zu lesen und sie mit der varispeed-Einheit
  5. zu verbinden
  6. Starten Sie das Diagramm, um mit der Wiedergabe zu beginnen
  7. Wenn Sie die Tonhöhe ändern möchten, tun Sie dies über AudioUnitSetParameter, und die Änderung der Tonhöhe und der Wiedergabegeschwindigkeit wird während der Wiedergabe von
  8. wirksam

Beachten Sie, dass es eine TimePitch-Audioeinheit gibt, die eine unabhängige Steuerung von Tonhöhe und Rate ermöglicht.

    
Michael Chinen 24.05.2014 21:30
quelle
2

Für iOS 7 sollten Sie den Zeitteilungsalgorithmus ( audioTimePitchAlgorithm ) von AVPlayerItem mit dem Namen AVAudioTimePitchAlgorithmVarispeed aufrufen. Leider ist diese Funktion auf frühen Systemen nicht verfügbar.

    
matt 24.05.2014 21:38
quelle