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.
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:
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.)
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.
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.
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.
Muss in iOS 6 und höher funktionieren, obwohl iOS 5-Unterstützung ein netter Bonus wäre.
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:
kAudioUnitSubType_RemoteIO
) hinzu (dies wird an den Sprecher ausgegeben) kAudioUnitSubType_Varispeed
) mit dem Eingang der RemoteIO-Einheit kAudioUnitSubType_AudioFilePlayer
) -Einheit hinzu, um die Datei zu lesen und sie mit der varispeed-Einheit Beachten Sie, dass es eine TimePitch-Audioeinheit gibt, die eine unabhängige Steuerung von Tonhöhe und Rate ermöglicht.
Tags und Links objective-c ios avaudioplayer core-audio openal