Ein einfacher Ton-Generator mit Python Im folgenden Beispiel wird ein einfacher Ton erzeugt. Jeder Ton soll eine Eigene Python Datei besitzen in der der Ton definiert wird. (Abschnitt 1) Danach werden die Datein in eine Hörbare WAV-Datei Umgewandelt.Dies geschieht mit der create.py Datei. Ziel war es mittels des einfachen Codes Töne zu erzeugen.
import numpy as np

SAMPLE_RATE = 44100

def sound():

    duration = 2

    t = np.linspace(
        0,
        duration,
        int(SAMPLE_RATE * duration),
        False
    )

    freq = 440

    signal = (
        1.0 * np.sin(2 * np.pi * freq * t)
        + 0.8 * np.sin(2 * np.pi * freq * 2 * t)
        + 0.6 * np.sin(2 * np.pi * freq * 3 * t)
        + 0.4 * np.sin(2 * np.pi * freq * 4 * t)
        + 0.2 * np.sin(2 * np.pi * freq * 5 * t)
    )

    env = np.ones(len(signal))

    fade_in = int(0.02 * SAMPLE_RATE)
    fade_out = int(0.20 * SAMPLE_RATE)

    env[:fade_in] = np.linspace(
        0,
        1,
        fade_in
    )

    env[-fade_out:] = np.linspace(
        1,
        0,
        fade_out
    )

    signal *= env

    signal /= np.max(np.abs(signal))

    return signal * 0.3

1. Abtastrate

SAMPLE_RATE = 44100

Erklärung

Die Abtastrate legt fest, wie viele Messwerte pro Sekunde berechnet werden. Eine Abtastrate von 44.100 Hz entspricht CD-Qualität. Für einen zwei Sekunden langen Ton werden somit 88.200 Samples erzeugt.


2. Dauer

duration = 2

Erklärung

Hier wird festgelegt, dass der Ton zwei Sekunden lang sein soll.


3. Zeitachse

t = np.linspace(
    0,
    duration,
    int(SAMPLE_RATE * duration),
    False
)

Erklärung

np.linspace() erzeugt die Zeitachse. Für jeden einzelnen Sample wird der genaue Zeitpunkt berechnet. Diese Werte werden später für die Sinusfunktion verwendet.


4. Frequenz

freq = 440

Erklärung

440 Hz entspricht dem musikalischen Ton A4 (Kammerton A). Die Schwingung wiederholt sich 440-mal pro Sekunde.


5. Sinuswellen

signal = (
    ...
)

Erklärung

Hier werden fünf Sinuswellen addiert:

Durch unterschiedliche Lautstärken der Obertöne entsteht ein komplexerer Klang als bei einer einzelnen Sinuswelle.


6. Hüllkurve

env = np.ones(len(signal))

Erklärung

Die Hüllkurve steuert die Lautstärke über die Zeit. Zunächst besitzt jeder Sample den Wert 1, wodurch das Signal unverändert bleibt.


7. Fade-In und Fade-Out

fade_in = int(0.02 * SAMPLE_RATE)
fade_out = int(0.20 * SAMPLE_RATE)

Erklärung

Am Anfang wird der Ton innerhalb von 20 ms langsam eingeblendet. Am Ende wird er innerhalb von 200 ms ausgeblendet. Dadurch entstehen keine hörbaren Knackgeräusche.


8. Hüllkurve anwenden

signal *= env

Erklärung

Jeder Sample des Tons wird mit dem entsprechenden Wert der Hüllkurve multipliziert. Dadurch verändert sich die Lautstärke über die Zeit.


9. Normalisierung

signal /= np.max(np.abs(signal))

Erklärung

Durch die Addition mehrerer Sinuswellen können Werte größer als 1 entstehen. Die Normalisierung skaliert das Signal so, dass die maximale Amplitude wieder genau 1 beträgt und kein Clipping auftritt.


10. Lautstärke

return signal * 0.3

Erklärung

Zum Schluss wird die Gesamtlautstärke auf 30 % reduziert. Dadurch bleibt die Form der Wellen unverändert, der Ton wird lediglich leiser.

WAV Creator

Dieses Programm dient zur automatischen Erstellung von WAV-Dateien aus Python-Sounddateien. Alle Python-Dateien im Ordner sounds werden erkannt und können über eine grafische Benutzeroberfläche ausgewählt und erstellt werden.

import os
import tkinter as tk
from tkinter import ttk, messagebox
import importlib
import numpy as np
from scipy.io.wavfile import write

SAMPLE_RATE = 44100


def create_wav():

    sound_name = combo.get()

    if not sound_name:
        messagebox.showwarning(
            "Hinweis",
            "Bitte einen Sound auswählen."
        )
        return

    sound_module = importlib.import_module(
        f"sounds.{sound_name}"
    )

    sound = sound_module.sound()

    sound /= np.max(np.abs(sound))

    audio = (sound * 32767).astype(np.int16)

    # Ordner "wav" erstellen, falls er nicht existiert
    os.makedirs("wav", exist_ok=True)

    base_name = sound_name

    output_file = os.path.join(
        "wav",
        f"{base_name}.wav"
    )

    counter = 1

    while os.path.exists(output_file):
        output_file = os.path.join(
            "wav",
            f"{base_name} ({counter}).wav"
        )
        counter += 1

    # WAV-Datei speichern
    write(
        output_file,
        SAMPLE_RATE,
        audio
    )

    messagebox.showinfo(
        "Fertig",
        f"{output_file} wurde erstellt."
    )


files = sorted([
    f[:-3]
    for f in os.listdir("sounds")
    if f.endswith(".py") and not f.startswith("__")
])

root = tk.Tk()
root.title("WAV Creator")
root.geometry("350x160")

ttk.Label(
    root,
    text="Sound auswählen:"
).pack(pady=(15,5))

combo = ttk.Combobox(
    root,
    values=files,
    state="readonly"
)
combo.pack(fill="x", padx=20)

if files:
    combo.current(0)

ttk.Button(
    root,
    text="WAV erstellen",
    command=create_wav
).pack(pady=20)

root.mainloop()