Dienstag 13. September 2016: Erstellen einer WAV-Datei
Digital Audio: Prinzipien
Schall: Schwankungen des Luftdrucks, erzeugt z.B. durch die Bewegung einer Lautsprechermembran, aufgenommen z.B. vom menschlichen Ohr.
Der Luftdruck (bzw. Auslenkung einer Membran) wird viele Male pro Sekunde gemessen (z.B. CD: 44100Hz). Diese Messpunkte (typischerweise vorzeichenbehaftete 16-Bit Ganzzahlen) werden gespeichert. Diese Messwerte werden Samples genannt. Die Umwandlung wird auch Sampling genannt.
Beim Abspielen, wird entsprechend dieser Werte eine Spannung erzeugt (und die Kurve zwischen Punkten geglättet).
WAV-Format
Das WAV-Format kann ziemlich komplex sein. Wir werden uns auf ein ganz einfaches Format beschränken:
- Mono (1 Kanal)
- 44100Hz Samplingrate
- unkomprimiert
- 16-Bit signed ints, little-endian
- Erzeugen von 2 Sekunden 440Hz-Ton (Kammer-a)
Wir gehen von folgender Beschreibung des WAV-Formats aus: https://de.wikipedia.org/wiki/RIFF_WAVE#Beispiel_eines_allgemein_lesbaren_WAVE-PCM-Formats
Ruby Details
String manipulation
Die Daten zuerst in einem String geschrieben. Damit das effizient ist, wird ein String der korrekten Länge angelegt:
filesize = ???? wav = 0.chr*filesize # String mit ASCII 0 gefüllt.
Der String kann dann direkt veränder werden, z.B. mit
wav[8..11]="WAVE" # Ersetze die Bytes 8 bis 11 mit den 4 Bytes der ASCII-Codes von "WAVE"
Little-Endian Kodierung
Da wir viele Werte Little-Endian kodieren werden müssen, wird eine Funktion definiert:
def to_le(wert, bytes=2) # Wird kein zweiter Parameter angegeben, ist dieser automatisch 2 res = 0.chr*bytes # String vorbereiten bytes.times{|i| # Bytes durchgehen res[i] = (wert & 0xff).chr # Wert in 8-Bit-ASCII umwandeln (nicht zwingend ein Symbol!) wert >>=8 # Kurzform für: wert = wert >> 8 } return res # Resultat der Funktion end #Zum Testen: puts to_le(1819242339, 4)
Konstanten des Programms
Wir definieren zuerst die Eckdaten unserer Wav-Datei:
duration = 2 format = 0x0001 # Siehe Dokumentation channels = 1 # Mono rate = 44100 # 44100Hz Samplingrate bytes_per_second = rate*2 blockalign = 2 bitssample = 16 filesize = ????? wav = 0.chr*filesize # # Hier die Header-Daten schreiben # Und dann Audio-Daten erzeugen #
Sinus
Um eine Schwingung darzustellen, kann die Sinus-Funktion verwendet werden (andere periodische Funktionen sind aber auch möglich, z.B. Sägezahn, Dreieck oder Rechteck).
sq32 = Math.sin(30/180.0*Math::PI)
Ausgabe in eine Datei
Die Datei wird in dem Verzeichnis erzeugt (bzw. überschrieben), wo das Programm gestartet wird.
File.open("output.wav","w"){|o| o.write(wav) }
Lösungsvorschlag
Weitere Aufgaben
- Experimentieren Sie mit anderen Funktionen. Fügen Sie z.B. Tremolo oder Vibrato dazu. Erzeugen Sie Rauschen.
- Erzeugen Sie zwei oder mehr Töne gleichzeitig (Funktionen einfach addieren, Aufgepasst auf Überläufe!).
Melodien codieren
- cdefgah stehen für die jeweiligen Töne. # bzw. b macht den folgenen Ton einen halben höher bzw. tiefer.
- + - wechselt eine Octave höher oder tiefer
- p steht für Pause
- 12345678 steht für die Länge in Achteln der folgenden Töne
Z.B. Alle meine Entchen ist dann
"2cdef4gg2aaaa8g2aaaa8g2ffff4ee2dddd8c"
Oder dieses hier:
"2ebeef4ea2ebeef4eba2ebeefe+d-h#gfed8c2a#ggf4a+d2-agfe4a+c2c-hah#d#faa+c-ha#ge#gh+e-2ebeef4ea2ebeef4eba2ebeefe+d-h#gfed8c2a#ggf4a+d2-agfe4a+c2c-hahe#gh+edc-h8a"
Schreiben Sie ein Programm, das die Melodie zum String erzeugt.