Skip to content
Fondazione ITS INCOM

Examples · AR/VR & Game Developer

Voice lines for your game, from a spreadsheet

Write your characters' lines in a spreadsheet, and one program turns every row into a WAV named after its id, ready to drop into Unity.

Intermediate · 40 min

The idea

A game has dozens of lines. For three, the Studio is enough; for fifty, write them in a spreadsheet, save it as CSV, and let a program make one file per line. Each file is named after the line's id, so the game finds it.

What you need

A key and the openai library, as in Your first API call in Python, and your lines in lines.csv. A spreadsheet saved as CSV works:

id,character,voice,text
inn_welcome,Innkeeper,amy,"Welcome to the Black Cat, traveller! A room is five silver coins a night."
inn_room,Innkeeper,amy,Your room is at the top of the stairs. Mind the cat.
guard_stop,Guard,ryan,Stop right there! Nobody enters the castle after sunset.
guard_pass,Guard,ryan,"The captain is expecting you. Go on, and keep your sword in its sheath."
villain_threat,Morvan,ryan,You came all this way for nothing. My army will burn this valley to ashes!

The voices in English are amy and ryan; characters can share one. The others are paola and riccardo for Italian, thorsten for German and siwis for French: write each line in the language of its voice.

The program

Save it as make_voices.py, next to lines.csv:

import csv
import os
from pathlib import Path

import openai
from openai import OpenAI

client = OpenAI(
    base_url="https://api.ai.itsincom.org/v1",
    api_key=os.environ["API_KEY"],
    max_retries=5,
)

out = Path("voices")
out.mkdir(exist_ok=True)

with open("lines.csv", newline="", encoding="utf-8-sig") as f:
    for row in csv.DictReader(f):
        path = out / f"{row['id']}.wav"
        if path.exists():
            continue  # already made: delete the file to get a new take
        try:
            speech = client.audio.speech.create(
                model="tts-1", voice=row["voice"], input=row["text"], response_format="wav",
            )
        except openai.BadRequestError as e:
            print(f"{row['id']}: not made. {e.body['message']}")
            continue
        speech.write_to_file(path)
        print(f"{path}  {row['character']}")

print("Done: the lines are in the voices folder.")

Run it with python3 make_voices.py.

What you should see

voices/inn_welcome.wav  Innkeeper
voices/inn_room.wav  Innkeeper
voices/guard_stop.wav  Guard
voices/guard_pass.wav  Guard
voices/villain_threat.wav  Morvan
Done: the lines are in the voices folder.

Five lines take a few seconds. Run the program again and only Done appears: every file is already there. Delete a file to get a new take of that line.

A row with a mistake, like the voice Amy with a capital letter, is skipped: the program prints why, with the list of valid voices (for now the message is in Italian), and goes on with the next row. Fix the row and run the program again: only the missing files are made.

Listen to them

When we transcribed our five files back, four said what was written, one with “Traveler” spelled the American way. Morvan's line came back as “My army will burn this valley's ashes”: maybe the voice swallowed the “to”, maybe the transcription misheard it. That is the line to listen to first.

In Unity

The program runs on your computer, before you build the game, so the key never goes into the game. Never put your key in a script or a file of a game: whoever has the build can read it, and spend your credits.

Good to know

Exercise

All examples