Zurück zur Homepage

Einen Beep via Soundkarte erzeugen

Es ist gar nicht so schwer, einen Ton via Soundkarte zu erzeugen. Man muss nur die richtigen API-Funktionen kennen. Durch Jürgen Wenzel bin ich an eine Mappe von Erich Neuwirth gekommen, der diese Funktionen dort einsetzt. Leider ist der Code dort nicht gerade leicht nachzuvollziehen, ich habs dann auch aufgegeben. Aber ein bischen im Internet gestöbert, die KB durchsucht und ein paar Stunden rumprobiert, das wars. Wobei mir allerdings wegen CopyMemory und Co. Excel mehrfach abgeschmiert ist. XP sei's gedankt, dass dann der Rest wenigstens stabil weiterläuft.

Beispieldatei (piep.zip 16 KB)

'########################################################
'# Zum Testen über Buttonklick, oder als benutzerdefinierte Funktion
'# aufrufen!                        =WENN(A4>10;Piepsen(1000;1000;2);)
'########################################################

Private Sub cmbPiepsen_Click()
Dim Frequenz As Long, Dauer As Long, Form As Long
Frequenz = Range("B1")
Dauer = Range("B2")
Form = Range("B3")
Piepsen Frequenz, Dauer, Form
End Sub 

'########################################################
'# In ein Modul
'########################################################

Private Declare Function waveOutReset Lib "winmm.dll" _
    (ByVal hWaveOut As Long) As Long
Private Declare Function waveOutOpen Lib "winmm.dll" _
    (lphWaveOut As Long, ByVal uDeviceID As Long, _
    lpFormat As waveformat_tag, ByVal dwCallback As Long, _
    ByVal dwInstance As Long, ByVal dwFlags As Long) As Long
Private Declare Function waveOutClose Lib "winmm.dll" _
    (ByVal hWaveOut As Long) As Long
Private Declare Function waveOutPrepareHeader Lib "winmm.dll" _
    (ByVal hWaveOut As Long, ByVal lpWaveOutHdr As Long, _
    ByVal uSize As Long) As Long
Private Declare Function waveOutUnprepareHeader Lib "winmm.dll" _
    (ByVal hWaveOut As Long, ByVal lpWaveOutHdr As Long, _
    ByVal uSize As Long) As Long
Private Declare Function waveOutWrite Lib "winmm.dll" _
    (ByVal hWaveOut As Long, ByVal lpWaveOutHdr As Long, _
    ByVal uSize As Long) As Long
Private Declare Sub CopyMemory Lib "KERNEL32" Alias "RtlMoveMemory" _
    (hpvDest As Any, hpvSource As Any, ByVal cbCopy As Long)
'Global Memory
Private Declare Function GlobalAlloc Lib "KERNEL32" _
    (ByVal wFlags As Long, ByVal dwBytes As Long) As Long
Private Declare Function GlobalFree Lib "KERNEL32" _
    (ByVal hMem As Long) As Long
Private Declare Function GlobalLock Lib "KERNEL32" _
    (ByVal hMem As Long) As Long
Private Declare Function GlobalUnlock Lib "KERNEL32" _
    (ByVal hMem As Long) As Long
Private Const GMEM_MOVEABLE = &H2
Private Const GMEM_SHARE = &H2000
Private Const WHDR_BEGINLOOP = &H4
Private Const WHDR_ENDLOOP = &H8
Private Const WAVE_ALLOWSYNC = 2
Private Const MMSYSERR_BASE = 0
Private Const WAVERR_BASE = 32
Private Const WAVE_FORMAT_QUERY = 1
Private Const WAVERR_STILLPLAYING = (WAVERR_BASE + 1)
Private Const MMSYSERR_INVALHANDLE = (MMSYSERR_BASE + 5)
Private Const MMSYSERR_NOMEM = (MMSYSERR_BASE + 7)
Private Const WAVERR_UNPREPARED = (WAVERR_BASE + 2)
Private Const WAVE_FORMAT_PCM = 1
'Typen
Private Type waveformat_tag
    wFormatTag As Integer
    nChannels As Integer
    nSamplesPerSec As Long
    nAvgBytesPerSec As Long
    nBlockAlign As Integer
    BitsPerSample As Integer
End Type
Private Type WaveHdr

    lpData As Long
    dwBufferLength As Long
    dwBytesRecorded As Long
    dwUser As Long
    dwFlags As Long
    dwLoops As Long
    lpNext As Long
    reserved As Long

End Type
Public Function Piepsen(Tonfrequenz As Long, Tondauer As Long, Optional Kurvenart As Long)
Dim WaveF As waveformat_tag, myHeader As WaveHdr
Dim myWave As Long
Dim memHeader As Long, pmemHeader As Long
Dim memDaten As Long, pmemDaten As Long
Dim Headerlänge As Long, Datenlänge As Long
Dim Daten As String, Durchläufe As Long
Dim ret As Long, i As Long, Grad As Long
Dim Abtastrate As Long, Auflösung As Long
If (Kurvenart > 3) Or (Kurvenart < 0) Then Kurvenart = 0
Headerlänge = Len(myHeader)
If Kurvenart = 0 And Tonfrequenz <= 4000 Then 'Sinus bis ca. 4 kHz
    Datenlänge = 24: Grad = 15
    If Tonfrequenz <= 2500 Then Datenlänge = 36: Grad = 10
    If Tonfrequenz <= 1250 Then Datenlänge = 72: Grad = 5
    If Tonfrequenz <= 500 Then Datenlänge = 180: Grad = 2
    For i = 1 To Datenlänge
        Daten = Daten & Chr((Sin(i * Grad * 3.1416 / 180) + 1) * 127)
    Next
ElseIf (Kurvenart = 0) And (Tonfrequenz > 4000) Then _
    Daten = Chr(0) & Chr(255) & Chr(0) '^ Dreieck bis ca. 33 kHz
ElseIf (Kurvenart = 1) Then _
    Daten = Chr(0) & Chr(255) & Chr(0) '^ Dreieck bis ca. 33 kHz
ElseIf (Kurvenart = 2) Then _
    Daten = Chr(0) & Chr(0) & Chr(255) & Chr(255) 'Rechteck bis ca. 25 kHz
ElseIf (Kurvenart = 3) Then _
    Daten = Chr(0) & Chr(127) & Chr(255)  'Sägezahn bis ca. 33 kHz
End If
Datenlänge = Len(Daten)
Abtastrate = Tonfrequenz * Datenlänge
Durchläufe = Tonfrequenz * Tondauer / 1000
With WaveF 'Waveformat
    .wFormatTag = WAVE_FORMAT_PCM
    .nChannels = 1 'Mono
    .nBlockAlign = 1  ' 8 bit mono
    .BitsPerSample = 8 'WAVE_FORMAT_8BIT
    .nSamplesPerSec = Abtastrate
'    .nAvgBytesPerSec = Abtastrate
End With
'Open Wave
'als myWave wird ein Wavehandle zurückgegeben
waveOutOpen myWave, 0&, WaveF, 0&, 0&, WAVE_ALLOWSYNC
'Globalen Speicher für Header erzeugen
memHeader = GlobalAlloc((GMEM_MOVEABLE Or GMEM_SHARE), Headerlänge)
pmemHeader = GlobalLock(memHeader)
'Globalen Speicher für Daten erzeugen
memDaten = GlobalAlloc((GMEM_MOVEABLE Or GMEM_SHARE), Datenlänge)
pmemDaten = GlobalLock(memDaten)
'Daten in den globalen Datenbuffer
CopyMemory ByVal pmemDaten, ByVal Daten, Len(Daten)
'Header setzen
With myHeader
    .dwBufferLength = Datenlänge
    .dwFlags = (WHDR_BEGINLOOP Or WHDR_ENDLOOP)
    .dwLoops = Durchläufe
    .lpData = pmemDaten 'Pointer auf den Datenblock
End With
'Den Header in den globalen Headerblock schreiben
CopyMemory ByVal pmemHeader, myHeader.lpData, Headerlänge
'Die Tonausgabe beginnen
waveOutPrepareHeader myWave, ByVal pmemHeader, Headerlänge
waveOutWrite myWave, ByVal pmemHeader, Headerlänge
'Auf das Beenden warten
Do
  ret = waveOutUnprepareHeader(myWave, ByVal pmemHeader, Headerlänge)
Loop While ((ret = WAVERR_STILLPLAYING) And (ret <> MMSYSERR_INVALHANDLE))
'waveOutReset .hWave ' Sofort Beenden
waveOutClose myWave
GlobalUnlock memDaten: GlobalFree memDaten
GlobalUnlock memHeader: GlobalFree memHeader
End Function