Amazon Polly in Blender

Letzte Woche hatte ich ein Whiteboard für meine Sofort-Videos installiert. Durch geschickte Positionierung der Textobjekte kann man durch eine Kamerafahrt in Blender die Einblendungen realisieren. Aber wie lange soll ein Text eingeblendet sein?

Timing mit Audio

Der Text sollte im Video mindestens so lange eingeblendet sein, wie man braucht, um ihn zu erfassen. Das hängt von der Lesegeschwindigkeit ab. Daher ist es eine gute Idee den Text zu vorzulesen und die Zeit zu stoppen. Das ist ein Teil des Editing.

Die Idee beim Sofortvideo war es aber sich die Zeit für das Editing zu sparen. Wir lassen uns den Text also vorlesen. Amazon hat mit Polly ein brauchbares Text-to-Speech (TTS) System im Angebot, auf das ich schon einen Blick geworfen hatte. Natürlich reizt es mich, das auszuprobieren.

Polly mit Boto3 in Blender

Boto3 ist das AWS SDK für Python. Damit ist es unglaublich einfach einen AWS Service in sein Python Programm einzubinden.

import boto3

pollyClient = boto3.client('polly')

Das ist alles? Naja, fast. Einerseits muss man sich noch in AWS authentifizieren. Am einfachsten geht das über die AWS Console. Damit konfiguriert man einen IAM Benutzer für die eigenen Konsole und eben auch Python.

Die zweite Hürde ist das Einbinden von Boto3 in Blender. In meinem Zauberspiegel Projekt hatte ich schon beschrieben, dass man ein Modul einfach in das Verzeichnis der Blender-Datei stellen muss. Da wir Blender im Hintergrund, also ohne Blender-Datei, ausführen, wäre das das Arbeitsverzeichnis des Python-Scripts. Boto3 hat aber so viele Abhängigkeiten, dass das keinen Spaß macht.

Man kann die Python Installation in Blender löschen. Blender verwendet dann ein selbst installiertes Python, in das man Boto3 einfach mit pip installieren kann.

Oder man installiert Boto3 mit pip in einer eigenen Python Installation und fügt den Pfad zu den installierten Paketen einfach im Script hinzu.

sys.path.append("<path-to>\site-packages")

Polly in Aktion

Die Sprachsynthese selbst ist ein einfacher API-Aufruf. Ich packe das Ganze in eine einfache function. Wir brauchen eigentlich nur den Sprecher und das Ausgabeformat angeben. Der Rest des Codes brauchen wir, um die Ausgabe in eine Datei zu schreiben:

def speak(speaker, text, filename):

    filename = os.getcwd() + '/renders/' + filename + '.mp3'

    with open(filename, 'wb') as f:
        # Request speech synthesis
        response = pollyClient.synthesize_speech(Text=text, VoiceId=speaker, OutputFormat='mp3')
        data_stream = response.get("AudioStream")

        while True:
            chunk = data_stream.read(16 * 1024)
            if not chunk:
                break
            f.write(chunk)

    return filename

Polly hat zwei deutsche Stimmen: die weibliche Marlene und den männlichen Hans. Lassen wir also Hans eine Frage stellen:

speak("Hans", "Hallo, wie geht es Dir?", "hans-hallo")

Audio in Blender

Bisher hatten wir nur eine Animation gerendert. Um Audio hinzuzufügen, müssen wir den Sequencer, also den Videoschnitt-Editor in Blender aufrufen. Das ist aber einfach:

scene = bpy.context.scene
if not scene.sequence_editor:
   scene.sequence_editor_create()

Und dann können wir den Audioclip von Hans an einem bestimmten Frame einfügen:

soundstrip = scene.sequence_editor.sequences.new_sound(text,filename,3,frame)

Die Länge des Clips gibt uns Blender in Frames an:

nextframe = frame + soundstrip.frame_duration

Damit wissen wir also schon, ab welchem Frame Marlene antworten kann.

Marlene antwortet

Wir positionieren die Kamera also hinter Hans Text und in einem Frame nach Hans Sprachausgabe. Dann folgt Marlenes Antwort: Sprachsynthese, Einbindung in den Sequencer und wieder Positionierung der Kamera hinter und nach der Antwort. Alles vollautomatisch:

# move camera

camera.location = (0,0,70)
camera.keyframe_insert(data_path="location",frame=nextframe)

# insert audio and calc frame

filename = speak("Marlene", "Hervorragend!", "marlene-hervorragend")
soundstrip = scene.sequence_editor.sequences.new_sound(text,filename,3,nextframe)

# calc next frame and position camera

nextframe += soundstrip.frame_duration+10

camera.location = (0,0,200)
camera.keyframe_insert(data_path="location",frame=nextframe)

Schließlich müssen wir beim Rendern noch sagen, dass wir auch Audio erzeugen möchten:

scene.render.ffmpeg.audio_codec = 'AAC'
scene.render.ffmpeg.audio_bitrate = 128

Das Ergebnis sieht dann so aus:

Fazit

Die Ansteuerung von Polly ist einfach. Selbst wenn man das Resultat nicht berauschend findet, kann man mit dieser Methode die Länge einer Einblendung bestimmen: man synthetisiert den Sprachertext und verwendet nur die Länge der Ausgabe für die Dauer der Einblendung.

Auf diese Weise ist tatsächlich einfach mit Blender einfache Videos vollautomatisch zu scripten. Nur einfache Videos? Blender kann fotorealistische Bilder rendern und hat die Tools für Visuelle Effekte bereits eingebaut. Wo ist das Limit?