Sinus-Ton ausgeben

  • Ich hab leider keinen Speaker im Lapotop.
    Kann es vllt daran liegen, dass AutoIt mit Radiant statt mit Grad rechnet?

    Klappts so : (wie gesagt - kann nicht testen) ?

    Spoiler anzeigen
    [autoit]

    Local $iDegToRad = 4 * ATan(1) / 180
    $ton = 0.5 * Sin((2 * $pi * 440 * $frequenz + 0) / $iDegToRad)

    [/autoit]
  • Bei deinem Code werden negative und sehr kleine Werte erzeugt, deshalb kann man nichts hören (Frequenz sollte zwischen 37 Hz - 32,767 kHz, wobei die Bandbreite für das menschliche Gehör irrelevant ist, da man zwischen 4Hz - 17 kHz i.d.R. nur hört)

    So kann ich auf meinem Schlepptop was hören:

    [autoit]


    $deg = ACos(-1) / 180
    $b = 1500
    For $i = 200 To 1000
    $f = Abs(Cos($i*$deg)*$b)
    ConsoleWrite($f & @CRLF)
    Beep($f, 20)
    Next

    [/autoit]

    Lustig wird's, wenn $deg = 1 ist. ;)

    Gruß,
    UEZ

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

    3 Mal editiert, zuletzt von UEZ (16. April 2010 um 11:24)

  • eukalyptus

    das klingt sehr logisch! Vielen Dank für dein Tip!

    Auch an die Anderen ein Dankeschön.

    PS ... ich such hier vergebens einen Schalter um den Beitrag als "gelöst" zu kennzeichnen.

  • Hi, habe passend zum Thema gestern schon etwas (wie üblich etwas längeres^^) verzapft, aber durch die Forensoftware wurde der Beitrag ans Nuldevice geschickt....

    Zunächst mal spielt der "Beep" eine Frequenz ab, ob die nun Sinusförmig ist, wage ich zu bezweifeln ^^, die Lautstärke (Amplitude) kannst du nicht beeinflussen.

    Um einen (Sinus-)Ton auf einem Lautsprecher auszugeben, mußt du also y(t) (die elektrische Spannung, die den Lautsprecher zum Schwingen anregt) ausrechnen und damit den Lautsprecher ansteuern. Damit die Membran des Lautsprechers schwingt, muss sich die Spannung ändern, ansonsten knackt es nur einmal kurz. Diese Spannung je nach der vergangenen Zeit wird mit der folgenden Formel ausgedrückt:

    y(t) = A * sin ( 2 * pi * f * t + phi)
    Das A bestimmt die Amplitude, d.h. die "Lautstärke" des Tons, das kannst du bei BEEP() nicht beeinflussen
    Die Frequenz f , nunja die wird nicht berechnet, sondern vorgegeben. f=1/T (T=Zeit von einem "Wellenberg" der Sinusfunktion zum nächsten)
    Die Phasenverschiebung phi ist im vorliegenden Fall (eine Schwingung) auch egal, da es deinem Ohr völlig schnurz ist, ob das Signal eine Millisekunde früher oder später kommt.
    Du kannst mit der Formel also den Wert auf der y-Achse zu jedem Zeitpunkt t errechnen.
    Die Formel reduziert sich zu y(t)=sin(2*pi*f*t)

    2*pi ist der Kreisumfang, also einmal die "Abwicklung" des Kreises auf der x-Achse. Da die Sinusfunktion sich immer wiederholt, müssen nur die y-Werte zum Zeitpunkt t während einer Schwingung (2*pi) berechnet werden.
    Gesucht ist also für jeden Zeitpunkt t die "Spannung" y(t), die an den Lautsprecher geschickt werden soll (diese Spannung lenkt die Membran aus!). Ist die Spannung y(t) über die Zeit t Sinusförmig, dann schwingt die Membrane und erzeugt einen "Sinuston". Soviel zur Theorie ^^

    AutoIt kommt ins Spiel...

    Spoiler anzeigen
    [autoit]

    $pi = atan(1)*4 ;3.14159265358979
    $pi = ATan(1) * 4
    $f = 100 ;frequenz in hertz
    $TT = 1 / $f ;T=1/f
    $w = 2 * $pi * $f ;omega=2*pi*f für Maschinenbauer gilt: OMEGA das weiß ich, Pi mal N durch dreißig!!! :o)
    $yt = ""

    [/autoit] [autoit][/autoit] [autoit]

    For $t = 0 To $TT Step $TT / 10 ;10 y(t)-Werte ()Amplituden) ermitteln für eine komplette sinusschwingung
    $yt &= Sin($w * $t) & @CRLF
    Next
    MsgBox(0, "Amplituden bei Frequenz " & $f & " Hz im Intervall 0-1/f", $yt)

    [/autoit] [autoit][/autoit] [autoit][/autoit]

    schön, wir haben nun 10 Amplituden unserer Sinusschwingung, wie bekommen wir die auf unserem Lautsprecher ausgegeben?
    Am einfachsten mit einer Wave-Datei (wav). Bei 8-Bit Mono entspricht jedes auf den DateiHeader folgende Byte einer Amplitude. HEUREKA! Also erstellen wir eine *.wav-Datei mit unseren Daten und lassen diese nun auf dem Soundsystem ausgeben.
    Allerdings reichen unsere 10 berechneten Amplituden bei weitem nicht aus, um einen "schönen" Ton zu erreichen...wir wählen 22050 Werte/Sekunde (Samplingrate). Geteilt durch eine gewähle Frequenz von 440Hz sind das rund 50 Werte PRO EINE SINUSSCHWINGUNG!

    Und hier kommt AutoIt ins Spiel.....

    Spoiler anzeigen
    [autoit]

    #include <Array.au3>
    #include <Memory.au3>
    #include <MemoryConstants.au3>

    [/autoit] [autoit][/autoit] [autoit]

    ;Das Script erstellt eine WAV-Datei im Speicher, spielt diese ab und speichert anschließend in eine Datei

    [/autoit] [autoit][/autoit] [autoit]

    $pi = 4 *atan(1) ;pi=3,1415926....
    $sekunden = 5 ;so lange soll der ton inb der abgespeicherten Datei abgespielt werden
    $samplingrate = 22050 ;abtastungen pro sekunde
    $BitsPerSample = 16 ;8,16,24 äquivalent die "Farbtiefe bei einem Bild",
    $Channels = 2 ;1,2,4, Kanäle, mono, stereo usw

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $block = Int($Channels * $BitsPerSample / 8) ;1
    $bytes = $samplingrate * $block
    $samplefaktor = $BitsPerSample / 8 ;lokale hilfsvariable
    $filesize = $samplefaktor * $sekunden * $samplingrate * $Channels + 44 - 8

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ;struktur mit dem Header einer WAV-Datei erstellen
    $struct = DllStructCreate("byte[" & $samplefaktor * $sekunden * $samplingrate * $Channels + 44 & "]") ;platz für unser sampling

    [/autoit] [autoit][/autoit] [autoit]

    $s = "char RIFF [4];" & _ ;RIFF
    "uint FileSize;" & _ ;Dateigröße-8 Bytes
    "char WAVE [4];" & _ ;WAVE
    "char fmt [4];" & _ ;fmt Header Signatur
    "uint fmt_len;" & _ ;länge restl. fmt
    "word Format;" & _ ;Datenformat s. Tabelle z.B. Wikipedia 1=PCM
    "word Channels;" & _ ;Anzahl Kanäle 1=MONO
    "uint Samplerate;" & _ ;Abtastrate pro Sekunde (22050)
    "uint Bytes;" & _ ;Abtastrate * Block (22050)
    "word Block;" & _ ;Channels * BitsPerSample / 8 (1)
    "word BitsPerSample;" & _ ;8, 16, oder 24 (8)
    "char DATA [4];" & _ ;DATA Header Signatur
    "uint Len;" ;Länge des folgenden Datenblocks

    [/autoit] [autoit][/autoit] [autoit]

    Switch $samplefaktor ;je nach einstellungen platz für die wav-Daten reservieren
    Case 1
    $s &= "byte WAVDATA[" & $sekunden*$samplefaktor * $samplingrate * $Channels & "]"
    Case 2
    $s &= "word WAVDATA[" & $sekunden*$samplefaktor * $samplingrate * $Channels & "]"
    Case 4
    $s &= "dword WAVDATA[" & $sekunden*$samplefaktor * $samplingrate * $Channels & "]"
    EndSwitch

    [/autoit] [autoit][/autoit] [autoit]

    $wavstruct = DllStructCreate($s, DllStructGetPtr($struct)) ;struct "überlagern" damit alle Daten byteweise in eine Datei geschrieben werden können

    [/autoit] [autoit][/autoit] [autoit]

    ;Unsere Daten für die Sinuswelle
    $f = 440 ;frequenz in hertz
    $TT = 1 / $f ;T=1/f
    $w = 2 * $pi * $f ;omega=2*pi*f für Maschinenbauer gilt: OMEGA das weiß ich, Pi mal N durch dreißig!!! :o)
    $Amplitude = 100 ^ $samplefaktor ;Lautstärke, bei 8 bit 100, bei 16 bit 10000
    $i=0 ;anzahl der geschriebenen bytes (funktionswerte)
    ;hier gehts los, wir erstellen eine komplette sinuswelle, diese wird so lange wiederholt, bis die Abspieldauer in sekunden erreicht ist
    For $t = 0 To $sekunden Step 1 / $samplingrate ;samplingrate y(t)-Werte ()Amplituden) ermitteln
    $i += 1
    $yt = Int($Amplitude * Sin($w * $t)) ;unsere altbekannte formel
    if $i<110 then consolewrite($i&" "&$yt & @CRLF )
    DllStructSetData($wavstruct, "WAVDATA", $yt, $i) ;schreiben von y(t) in die struct
    If $Channels = 2 Then ;wenn 2 kanäle (stereo), dann das byte für den 2. Kanal hinzufügen
    $i += 1
    DllStructSetData($wavstruct, "WAVDATA", $yt, $i)
    EndIf
    Next

    [/autoit] [autoit][/autoit] [autoit]

    ;jetzt werden die Daten in die struct geschrieben, es entsteht eine WAV-Datei im Speicher
    DllStructSetData($wavstruct, "RIFF", "RIFF")
    DllStructSetData($wavstruct, "FileSize", $filesize)
    DllStructSetData($wavstruct, "WAVE", "WAVE")
    DllStructSetData($wavstruct, "fmt", "fmt ")
    DllStructSetData($wavstruct, "fmt_len", 16)
    DllStructSetData($wavstruct, "Format", 1) ;PCM
    DllStructSetData($wavstruct, "Channels", $Channels)
    DllStructSetData($wavstruct, "Samplerate", $samplingrate)
    DllStructSetData($wavstruct, "Bytes", $bytes) ;samplerate*block
    DllStructSetData($wavstruct, "Block", $block)
    DllStructSetData($wavstruct, "BitsPerSample", $BitsPerSample)
    DllStructSetData($wavstruct, "DATA", "data")
    DllStructSetData($wavstruct, "Len", Int($samplingrate / $f * $Channels * $samplefaktor));hier wird NUR DIE LÄNGE DER ERSTEN SINUSWELLE eingetragen, um den Sound im speicher abzuspielen

    [/autoit] [autoit][/autoit] [autoit]

    $data = ""
    For $i = 1 To 14 ;anzeigen des WAV-Headers
    $data &= DllStructGetData($wavstruct, $i) & @CRLF
    Next
    MsgBox(0, "Daten WAV-Header", StringLeft($data, 100))

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _Playsound(DllStructGetPtr($wavstruct), 1) ;abspielen des sounds aus dem speicher, Ton wird immer wiederholt
    Msgbox(0,"Ton mit Frequenz "&$f&" Hz","Spieldauer "&$sekunden&" Sekunden",$sekunden)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ;sound mit Länge von Sekunden in Datei abspeichern ; dafür muss die länge des sounds in der struct geändert werden von einer wellenlänge(s.o.) zur Länge aller wellen bis zum ende der spielzeit
    DllStructSetData($wavstruct, "Len", Int($sekunden *$samplingrate * $Channels * $samplefaktor));hier wird NUR DIE LÄNGE DER ERSTEN SINUSWELLE eingetragen, um den Sound im speicher abzuspielen

    [/autoit] [autoit][/autoit] [autoit]

    $sdat = DllStructGetData($struct, 1) ;struktur in eine Datei schreiben
    $wavdata = StringLeft(BinaryToString($sdat), $sekunden *$samplingrate * $Channels * $samplefaktor + 44)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $wavdata = ' & StringLen($wavdata) & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    $wav = FileOpen("test1.wav", 18)
    $bindata = FileWrite($wav, $wavdata)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $bindata = ' & $bindata & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    FileClose($wav)

    [/autoit] [autoit][/autoit] [autoit]

    shellexecute("test1.wav") ;wavedatei abspielen
    exit

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _PlaySound($sSoundFile, $mem = 0) ;mem=1 spielt sound aus dem speicher, dann muss $sSoundfile der pointer zum anfang der struct sein
    Local Const $SND_ALIAS = 0x10000
    Local Const $SND_ALIAS_ID = 0x110000
    Local Const $SND_APPLICATION = 0x80
    Local Const $SND_ASYNC = 0x1
    Local Const $SND_FILENAME = 0x20000
    Local Const $SND_LOOP = 0x8
    Local Const $SND_MEMORY = 0x4
    Local Const $SND_NODEFAULT = 0x2
    Local Const $SND_NOSTOP = 0x10
    Local Const $SND_NOWAIT = 0x2000
    Local Const $SND_PURGE = 0x40
    Local Const $SND_RESOURCE = 0x40004
    Local Const $SND_SYNC = 0x0
    If $mem = 1 Then
    $a = DllCall('winmm.dll', 'int', 'PlaySoundA', 'ptr', $sSoundFile, 'int', 0, 'int', BitOR(4, 1, 8, 2, 0x10))

    [/autoit] [autoit][/autoit] [autoit]

    Else
    $a = DllCall('winmm.dll', 'int', 'PlaySoundA', 'str', $sSoundFile, 'int', 0, 'int', BitOR(1, 8))
    EndIf
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $a = ' & $a[0] & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    EndFunc ;==>_PlaySound

    [/autoit]

    Hausaufgabe: bastel eine schöne GUI^^