def get_frequency(ton, pm, oktave) tabelle = {'a'=>0, 'h'=>2, 'c'=>-9, 'd'=>-7, 'e'=>-5, 'f'=>-4, "g"=>-2} return 0.0 if ton=='p' halbton = tabelle[ton]+pm+12*oktave lambda = 2.0**(1.0/12.0) return 440.0*(lambda**halbton); end # Schwingungsfunktion zum Zeitpunkt t mit Frequenz freq # Werte zwischen -1 und 1 def get_function(freq, t) return Math.sin(freq*2*t*Math::PI)*(0.5**(t/0.1)); end # Liefert ein Array mit Ganzzahlen zurück # (zwischen min -32000 und max 32000) def get_samples(freq, dauer, rate=44100) anzahl = (dauer*rate).to_i return Array.new(anzahl) {|i| (get_function(freq, i.to_f/rate)*20000).to_i } end # to_le wandelt einen Zahlwert (wert) in ein Folge von einer gegbenen (bytes) Bytes um. # Z.B. wird mit to_le(0x1020,4) die Folge 0x20,0x10,0x00,0x00 von 4 Bytes erzeugt. 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 # lied: String # Ausgabe: Array mit Ganzzahlen def play(lied, dauer=0.2, rate=44100) achtel = 2 pm = 0 oktave = 0 samples = [] lied.each_char{|c| case c when 'b' pm=-1 when '#' pm=1 when '+' oktave+=1 when '-' oktave-=1 when '1'..'9' achtel = c.to_i else freq = get_frequency(c, pm, oktave); samples+=get_samples(freq, achtel*dauer, rate); end } return samples end format = 0x0001 # Siehe Dokumentation channels = 1 # Mono rate = 44100 # 44100Hz Samplingrate bytes_per_second = rate*2 blockalign = 2 bitssample = 16 lied = "2cdef4gg2aaaa8g2aaaa8g2ffff4ee2dddd8c" samples = play(lied,0.1) # Uwandeln, zusammenfügen als String samples = samples.map{|s| to_le(s,2)}.join("") filesize = 44 + samples.size wav = 0.chr*44 wav[0..3] = "RIFF" wav[4..7] = to_le(filesize-8, 4) wav[8..11]="WAVE" wav[12..15] = "fmt " wav[16..19] = to_le(16,4) wav[20..21] = to_le(format,2) wav[22..23] = to_le(channels,2) wav[24..27] = to_le(rate,4) wav[28..31] = to_le(bytes_per_second,4) wav[32..33] = to_le(blockalign,2) wav[34..35] = to_le(bitssample,2) wav[36..39] = "data" wav[40..43] = to_le(filesize-44,4) # Wav-Datei effektiv abspeichern File.open("output.wav","w"){|o| o.write(wav) # Header o.write(samples) # PCM-Daten }