Die Community zu .NET und Classic VB.
Menü

DirectSound

 von 

Übersicht 

Wie der Name es schon vermuten lässt, ist DirectSound in der Lage Sound (Musik/Geräusche) über die Soundkarte auszugeben. Doch das ist noch längst nicht alles! DirectSound bietet auch die Möglichkeit die Soundquelle in einen 3D Raum zu stellen, wodurch ein Raumklang erzeugt werden kann. Durch Effektfilter lassen sich Klänge verzerren. Es ist sogar möglich selbst Sounds aufzunehmen. Bis auf Direct3D Sound (aufgrund seines Umfangs) werden all diese Techniken in diesem Tutorial besprochen.

Die Soundkarte  

Wie man es von DirectX gewohnt ist, bietet es zu jedem Bereich die Möglichkeit die verfügbaren Geräte auflisten zu lassen, so auch bei DirectSound:
Bei DirectSound wird dabei zwischen Aufnahme- (Capture) und Soundgeräten unterschieden. Als erstes wollen wir ein kleines Programm schreiben, welches die verfügbaren Geräte auflistet.

Erstellen Sie dazu bitte ein neues Projekt und binden im Menü Projekt à Verweise die DirectX 8 Type Library ein. Anschließend fügen Sie dem Formular zwei Comboboxen hinzu, die Sie „cboSound“ und „cboCapture“ nennen und schreiben folgenden Code in das Formular:

Option Explicit

'//Das Ausgangsobjekt, aus dem
'//alle anderen DirectX-Objekte
'//hervorgehen
    Dim objDX As New DirectX8

'//Soll die Auflistung aller
'//DirectSound Geräte enthalten
    Dim objDSs As DirectSoundEnum8 'Soundgeräte
    Dim objDSc As DirectSoundEnum8 'Aufnahmegeräte

Private Sub Form_Load()

    '//Auflistungen von DirectX holen
    Set objDSs = objDX.GetDSEnum()
    Set objDSc = objDX.GetDSCaptureEnum()

    '//Zurückgelieferte Geräte in Comboboxen anzeigen
    Dim i As Integer

      'Soundgeräte
      For i = 1 To objDSs.GetCount()
        cboSound.AddItem objDSs.GetDescription(i)
      Next i

      'Aufnahmegeräte
      For i = 1 To objDSc.GetCount()
        cboCapture.AddItem objDSc.GetDescription(i)
      Next i

      'ersten Einträge aus Combobox selektieren
      cboSound.ListIndex = 0
      cboCapture.ListIndex = 0
End Sub

Einfaches Abspielen von Sounds  

Das nächste Beispiel soll zeigen, wie man einfache Sounds abspielen kann. Erstellen Sie dazu bitte wieder ein neues Projekt, fügen den Verweis auf die DirectX 8 Type Library hinzu und erstellen zwei Buttons auf einem Formular (cmdStart und cmdStop). Der Quelltext der Andwendung sieht folgendermaßen aus. Alle mit einem „<--“ versehenen Stellen werden hinterher erklärt:

Option Explicit

'//Unser Grundobjekt
  Dim objDX As New DirectX8

'//Die Verbindung zu Soundkarte
  Dim objDS As DirectSound8

'//Spiegelt unseren Sound (eine
'//Wave-Datei) wieder
  Dim objDSBuff As DirectSoundSecondaryBuffer8

Private Sub Form_Load()

  'Struktur die unseren SoundBuffer beschreibt
  Dim descSoundBuff As DSBUFFERDESC
  'Der Dateiname mit Pfad der Wav-Datei
  Dim strFilename As String

    '//DirectSound instanzieren
    Set objDS = objDX.DirectSoundCreate(vbNullString) '<--

    '//DirectSound mit unserer Anwendung verbinden
    objDS.SetCooperativeLevel Me.hWnd, DSSCL_NORMAL '<--

    '//Einen Soundbuffer erstellen, der aus einer
    '//Wav-Datei geladen wird
    strFilename = App.Path & "\yippee.wav"
    Set objDSBuff = objDS.CreateSoundBufferFromFile(strFilename,
                      descSoundBuff) '<--
End Sub

Private Sub cmdPlay_Click()

  '//Soundbuffer abspielen
  objDSBuff.Play DSBPLAY_DEFAULT '<--

End Sub

Private Sub cmdStop_Click()

  '//Soundbuffer anhalten und auf Anfang setzen
  objDSBuff.Stop 'Stop = Pause!!
  objDSBuff.SetCurrentPosition 0 'Auf Anfang zurück

End Sub

Erklärungen

  1. Set objDS = objDX.DirectSoundCreate(vbNullString)

    Hier wird eine Verbindung zur Soundkarte hergestellt. Über das DirectSound-Objekt (objDS) können dann später alle Sounds abgespielt werden. Das vbNullString gibt hierbei das Gerät (die Soundkarte) an, die wir verwenden wollen. vbNullString entspricht hierbei dem Standardgerät. Wenn Sie andere Geräte verwenden wollen, müssen Sie den GUID dieses Gerätes übergeben. Sie erhalten ihn indem Sie die Geräte über die Enumeration (siehe Kapitel 1) auflisten lassen und anstatt der GetDescription() die GetGuid() Methode aufrufen (siehe Quelltext Kapitel 1). Statt vbNullString könnten Sie demnach auch „{00000000-0000-0000-0000-000000000000}“ schreiben.

  2. objDS.SetCooperativeLevel Me.hWnd, DSSCL_NORMAL

    Hier verbinden wir DirectSound mit unserem Fenster, indem wir ihm das Handle des Fensters übergeben. Der zweite Parameter gibt an, wie DirectSound mit der Anwendung verbunden ist. Es gibt dazu insgesamt 3 Möglichkeiten:

    DSSCL_NORMAL Anwendung kann die SetFormat() Methode des DirectSound8PrimaryBuffer8 – Objekts nicht aufrufen! Das Format der Sounddaten ist auf das 8Bit Format festgelegt. Dafür ist Speicherverwaltung in diesem Modus die beste.
    DSSCL_PRIORITY Die SetFormat() Methode kann aufgerufen werden um das Format des Sounddaten zu ändern.
    DSSCL_WRITEPRIMARY Der primäre Soundbuffer kann selbst beschrieben werden.
  3. objDSBuff.Play DSBPLAY_DEFAULT

    Startet die Wiedergabe des Soundbuffers (es können auch mehrere Soundbuffer gleichzeitig abgespielt werden). Der Parameter gibt an, wie die Bufferdaten abgespielt werden sollen, es gibt hierbei (unter anderem) folgende Möglichkeiten:

    DSBPLAY_DEFAULT Startet die Wiedergabe ab Cursorposition (kann mit .SetCurrentPosition() gesetzt werden) und spielt bis zum Ende ab.
    DSBPLAY_LOOPING Spielt ab Cursorposition bis zum Ende, und wiederholt die Wiedergabe (von Anfang des Buffers)
    DSBPLAY_TERMINATEBY_TIME ??????
  4. objDSBuff.Stop objDSBuff.SetCurrentPosition 0

    In der ersten Zeile wird die Wiedergabe angehalten. Hierbei ist zu beachten, dass Stop im Sinne von Pause gemeint ist, der Buffer wird nicht automatisch auf den Anfang zurückgesetzt! In der zweiten Zeile wird der Cursor dann manuell auf den Anfang gesetzt.

Sonstige Dinge zum Abspielen  

Lautstärke einstellen

Die Lautstärke wird in DirectSound in 100stel Dezibel angegeben, 0 (oder DSBVOLUME_MAX) entspricht der original Lautstärke des Sounds, -10 000 (oder DSBVOLUME_MIN (entspricht –100db)) ist das Leiseste. Gesetzt wird die Lautstärke mit der objDSBuff.SetVolume (Dezibel) Methode, gelesen mit objDSBuff.GetVolume().

Beispiel:
'//Lautstärke um 1 db erhöhen
objDSBuff.SetVolume (objDSBuff.GetVolume() + 100)

Balance einstellen

Es ist auch möglich die Lautstärke der einzelnen Lautsprecher zu ändern. Dabei bleibt ein Kanal immer auf normal Lautstärke und der andere wird um die angegebene Dezibel Zahl leiser gemacht (Angabe in 100stel db). Das Vorzeichen (+ oder -) der Angabe entscheidet, welcher Kanal leiser wird. 0 bedeutet folglich, dass beide Kanäle gleich laut sind. Die Methode dazu ist die objDSBuff.SetPan (Dezibel). Die Konstanten DSBPAN_LEFT, DSBPAN_RIGHT und DSBPAN_CENTER können auch verwendet werden.

Beispiel:
'//Linken Lautsprecher um 10db leiser machen
objDSBuff.SetPan (objDSBuff.GetPan() - 1000)

Frequenz ändern

Das Ändern der Frequenz wirkt sich auf die Abspielgeschwindigkeit aus. Erhöht man die Frequenz, werden pro Sekunde mehr Samples abgespielt und somit die Wiedergabe schneller. Die Einheit für die Frequenz sind Hertz. Das Minimum sind 100hz (DSBFREQUENCY_MIN) und das Maximum sind 100000hz (DSBFREQUENCY_MAX). Um die original Frequenz wiederherzustellen, kann die DSBFREQUNCY_ORIGINAL Konstante verwendet werden. Gesetzt wird die Frequenz mit der objDSB.SetFrequency (Hertz) – Methode.

Beispiel:
'//Frequenz um 100hz erhöhen
objDSBuff.SetFrequency (objDSBuff.GetFrequency() + 100)

Effektfilter  

DirectSound bietet standardmäßig einige Effektfilter, die sich auf einfache Weise auf einen SoundBuffer anwenden lassen. Hier ein Beispiel dazu:
(1 Form, 2 Listboxen (lstEffekte, lstGewaehlt), 3 CommandButtons (cmdAdd, cmdRemove, cmdPlay))

Option Explicit

'//Die üblichen Objekte
  Dim objDX As New DirectX8
  Dim objDS As DirectSound8
  Dim objDSBuff As DirectSoundSecondaryBuffer8

Private Sub cmdAdd_Click()

  '//Effekt der "Gewählt-Liste" hinzufügen
  lstGewaehlt.AddItem lstEffekte.List(lstEffekte.ListIndex)
  lstEffekte.RemoveItem lstEffekte.ListIndex

End Sub

Private Sub cmdPlay_Click()
  '//Für jeden Effekt ein Deskriptor
  Dim descEffect() As DSEFFECTDESC
  Dim lngResult() As Long 'Speichert Ergebnis der SetFX-Methode
  Dim i As Integer 'Schleifenzähler

    '//Falls Wiedergabe noch läuft
    objDSBuff.Stop
    objDSBuff.SetCurrentPosition 0 'auf Anfang zurück

    '//Wenn keine Effekte gewählt sind, Effekte ausschalten
    If lstGewaehlt.ListCount = 0 Then
      objDSBuff.SetFX 0, descEffect, lngResult 'FX aus
    Else
      '//Für jeden Eintrag eine Effektfilter erstellen
      ReDim descEffect(lstGewaehlt.ListCount - 1)
      ReDim lngResult(lstGewaehlt.ListCount - 1)

      '//Alle selektierten Filter aktivieren
      For i = 0 To lstGewaehlt.ListCount - 1
        Select Case lstGewaehlt.List(i)
          Case "Echo":
            descEffect(i).guidDSFXClass = _
               DSFX_STANDARD_ECHO
          Case "Chor":
            descEffect(i).guidDSFXClass = _
               DSFX_STANDARD_CHORUS
          Case "Gargle":
            descEffect(i).guidDSFXClass = _
               DSFX_STANDARD_GARGLE
          Case "Flanger":
            descEffect(i).guidDSFXClass = _
               DSFX_STANDARD_FLANGER
          Case "Distortion":
            descEffect(i).guidDSFXClass = _
               DSFX_STANDARD_DISTORTION
          Case "Compressor":
            descEffect(i).guidDSFXClass = _
               DSFX_STANDARD_COMPRESSOR
          Case "ParamEQ":
            descEffect(i).guidDSFXClass = _
               DSFX_STANDARD_PARAMEQ
          Case "WavesReverb":
            descEffect(i).guidDSFXClass = _
               DSFX_STANDARD_WAVES_REVERB
        End Select
      Next i

      '//Effekte aktivieren
      objDSBuff.SetFX lstGewaehlt.ListCount, descEffect, lngResult

      '//Resourcen anfordern
      objDSBuff.AcquireResources 0, lngResult

    End If

    '//Abspielen
    objDSBuff.Play DSBPLAY_DEFAULT
End Sub

Private Sub cmdRemove_Click()
  '//Effekt wieder aus Liste entfernen
  lstEffekte.AddItem lstGewaehlt.List(lstGewaehlt.ListIndex)
  lstGewaehlt.RemoveItem lstGewaehlt.ListIndex
End Sub

Private Sub Form_Load()
  Dim descSoundBuff As DSBUFFERDESC

    '//DirectSound initialisieren und Wavedatei laden
    '//(Erklärungen siehe Kapitel 2)
    Set objDS = objDX.DirectSoundCreate(vbNullString)

    '//Deskriptor für SoundBuffer konfigurieren
    '//wir geben an, was wir kontrollieren wollen
    descSoundBuff.lFlags = DSBCAPS_CTRLFX Or DSBCAPS_LOCDEFER
    objDS.SetCooperativeLevel Me.hWnd, DSSCL_NORMAL

    Set objDSBuff = objDS.CreateSoundBufferFromFile(App.Path &
        "\sound.wav", descSoundBuff)
End Sub

Erläuterungen

Zunächst erstellen wir wie üblich die DirectSound Objekte und laden die Wavedatei in einen Soundbuffer. Diesmal müssen wir aber das lFlags Attribut des Soundbuffer-Deskriptors festlegen, da wir auf die SetFX() und die AcquireResources() Methoden zugreifen wollen. Vergessen wir den Flag zu setzen, so wird ein Automatisierungsfehler ausgelöst, sobald wir die genannten Methoden versuchen aufzurufen.

Um die Effektfilter jetzt zu aktivieren, müssen wir für jeden Effekt eine eigene Struktur definieren (bei mehreren Effektfiltern als Array!). Dem guidDSFXClass – Attribut weisen wir eine Konstante zu, die den Effekt definiert. Folgende sind dabei erlaubt:

DSFX_STANDARD_CHORUS Hall Effekt
DSFX_STANDARD_COMPRESSOR
DSFX_STANDARD_DISTORTION
DSFX_STANDARD_ECHO Echo Effekt
DSFX_STANDARD_FLANGER
DSFX_STANDARD_GARGLE
DSFX_STANDARD_I3DL2REVERB
DSFX_STANDARD_PARAMEQ
DSFX_STANDARD_WAVES_REVERB

Um die Effekte jetzt endgültig auf den Soundanzuwenden müssen wir noch die SetFX() – Methode aufrufen. Als erstes Argument muss die Anzahl der Effekte, die sich im Array befinden und aktiviert werden sollen, angegeben werden. Das zweite Argument enthält das Array mit den Filtern und als letztes Argument wird ein Array gleicher Dimension erwarten in dem für jeden Effekt ein Rückgabewert gespeichert wird. Folgende Rückgabewerte sind möglich:

DSFXR_LOCHARDWARE Der Effektfilter wurde erfolgreich initialisiert und wird von der Hardware berechnet
DSFXR_LOCSOFTWARE Der Effektfilter wurde erfolgreich initialisiert, kann aber nur von der Software emuliert werden
DSFXR_UNALLOCATED Der Effekt wurde weder für Hard- noch für Software erstellt
DSFXR_FAILED Es konnte kein Effekt erstellt werden, da keine Resourcen zur Verfügung stehen
DSFXR_PRESENT Aus irgendeinem Grund konnte der Effekt nicht erstellt werden
DSFXR_UNKNOWN Der angegebene Effekt ist auf dem System nicht registriert.

Zuletzt müssen wir noch angeben, wie die Resourcen zum Abspielen alloziiert werden müssen. Dies geschieht mit dem AcquireResources 0, lngResults Aufruf.

Aufnahme  

Zuletzt wollen wir eine Möglichkeit kennen lernen, Sounds über ein Mikrofon (oder andere Eingabegeräte) aufzunehmen und diesen dann als WaveDatei abzuspeichern. Für das nachfolgende Beispiel wird allerdings eine Soundkarte benötigt, gleichzeitiges Abspielen und Aufnehmen erlaubt, da wir gleichzeitig eine Instanz eines Wiedergabe- und eines Aufnahmeobjekts erstellen. Sollte die Soundkarte dieses Feature nicht unterstützen, wird sich das Programm mit einem Automatisierungsfehler beenden. Für das Beispiel benötigen wir ein Formular mit 3 CommandButtons (cmdRecord, cmdStop, cmdSave):

Option Explicit

'//Die üblichen Objekte
  Dim objDX As New DirectX8
  Dim objDS As DirectSound8
  Dim objDSBuff As DirectSoundSecondaryBuffer8

'//Ein Aufnahmeobjekt und ein Buffer
'//auf den aufgenommen wird
  Dim objDSCap As DirectSoundCapture8
  Dim objDSCapBuff As DirectSoundCaptureBuffer8

Private Sub cmdSave_Click()
  Dim descBuff As DSBUFFERDESC 'beschreibt den Sec.Buffer
  Dim waveFormat As WAVEFORMATEX 'beschreibt die Aufnahme
  Dim curInfo As DSCURSORS

    '///////////////////////////////////////////////////
    '//zuerst müssen wir den Capture- in eine SecondaryBuffer
    '//umwandeln, damit wir ihn speichern können

        'Informationen über den CaptureBuffer erhalten
        objDSCapBuff.GetCurrentPosition curInfo

        'Deskriptor für Sec.Buffer erstellen
        With descBuff
          'Die benötigte Größe bestimmen
          .lBufferBytes = curInfo.lWrite + 1

          'das Format muss übernommen werden
          objDSCapBuff.GetFormat waveFormat
          .fxFormat = waveFormat
        End With

        'anhand dieser Daten den Sec.Buffer erstellen
        Set objDSBuff = objDS.CreateSoundBuffer(descBuff)

        'jetzt müssen noch die Daten rüberkopiert werden
        Dim bytes() As Integer

          ReDim bytes(curInfo.lWrite)
    objDSCapBuff.ReadBuffer 0, UBound(bytes), bytes(0),
                            DSCBLOCK_DEFAULT
          objDSBuff.WriteBuffer 0, UBound(bytes), _
                            bytes(0), DSBLOCK_DEFAULT

    '///////////////////////////////////////////////////
    'Abspeichern
    objDSBuff.SaveToFile App.Path & "\wave.wav"
End Sub

Private Sub cmdRecord_Click()
  '//wir starten die Aufnahme
  objDSCapBuff.Start DSCBSTART_DEFAULT

  '//Buttons an/ausschalten
  cmdRecord.Enabled = False
  cmdSave.Enabled = False
  cmdStop.Enabled = True
End Sub

Private Sub cmdStop_Click()
  '//Aufnahme beenden
  objDSCapBuff.Stop

  '//Buttons an/ausschalten
  cmdRecord.Enabled = True
  cmdSave.Enabled = True
  cmdStop.Enabled = False
End Sub

Private Sub Form_Load()
  Dim descCapBuff As DSCBUFFERDESC
'Wie soll unser Buffer aussehen?
  Dim capFormat As WAVEFORMATEX
'Was für ein Format soll die Wavedatei haben?

    '//Das Aufnahmeobjekt erstellen
    '//wir verwenden das Standardgerät (vbNullString)
    Set objDSCap = objDX.DirectSoundCaptureCreate(vbNullString)

    '//Das Wiedergabeobjekt erstellen
    Set objDS = objDX.DirectSoundCreate(vbNullString)

    '//Format festlegen, in dem wir aufnehmen wollen
    With capFormat
      .nFormatTag = WAVE_FORMAT_PCM
      .nChannels = 1 'wir nehmen in mono auf
      .lSamplesPerSec = 44100
      .nBitsPerSample = 16
      .nBlockAlign = .nChannels * .nBitsPerSample / 8
      .lAvgBytesPerSec = .lSamplesPerSec * .nBlockAlign
      .nSize = 0
    End With

    '//Unseren Aufnahmebuffer für DX "beschreiben"
    With descCapBuff
      .fxFormat = capFormat
      .lBufferBytes = capFormat.lAvgBytesPerSec * 20
      .lFlags = DSCBCAPS_WAVEMAPPED
    End With

    '//Den Buffer erstellen, auf den wir aufnehmen können
    Set objDSCapBuff = objDSCap.CreateCaptureBuffer(descCapBuff)
End Sub

Wie auch bei Abspielen, so brauchen wir auch zum Aufnehmen ein Aufnahmeobjekt (welches die Soundkarte repräsentiert) und einen Buffer auf den wir aufnehmen können. Mit dem Aufruf von Set objDSCap = objDX.DirectSoundCaptureCreate(vbNullString) stellen wir die Verbindung zur Soundkarte her, vbNullString bedeutet wie immer das Standardgerät, soll ein anderes Gerät verwendet werden, muss dessen GUID andgegeben werden (siehe Kapitel 2). Den Aufnahmebuffer erstellen wir mit der CreateCaptureBuffer() – Methode des zuvor erstellten Capture-Objekts. Die Methode erwartet ein Argument vom Typ DSCBUFFERDEC (Direct Sound Capture Buffer Descriptor). Sie enthält Informationen über das Aufnahmeformat (z.B die Anzahl der Kanäle (Stereo/Mono), die Bitrate, Samples / s, etc). Nachdem wir diesen Buffer erfolgreich instanziert haben, können wir die Aufnahme mit der Start() – Methode des Bufferobjektes beginnen. Mit der Stop() – Methode beenden wir sie. Da man einen Aufnahmebuffer weder abspielen, noch etwas anderes mit ihm als aufnehmen kann, müssen wir ihn in einen SecondaryBuffer (einen Wiedergabebuffer, bekannt aus den vorrangegangenen Kapiteln) umwandeln. Dazu erstellen wir zunächst einen Deskriptor für den SecondaryBuffer. Er muss natürlich das gleiche Format und die gleiche Größe wie der AufnahmeBuffer haben. Anschließend wird mit der CreateSoundBuffer() – Methode des DirectSound-Objekts der SecondaryBuffer erstellt. Als Parameter übergeben wir der Methode des zuvor erstellten Deskriptor. Jetzt haben wir zwei Buffer (einen Aufnahme- und einen Wiedergabebuffer) gleicher Größe und gleichen Formats. Es müssen also nur noch die Daten vom Aufnahme- in den Wiedergabebuffer kopiert werden. Dazu wird ein Integer-Array der Größe des Aufnahmebuffers erstellt. Mit der ReadBuffer() – Methode des Aufnahmebuffers lesen wir die Binärdaten in das Array ein und schreiben sie anschließend mit der WriteBuffer() – Methode des Wiedergabebuffers in den Wiedergabebuffer. Zuletzt speichern wir den Wiedergabebuffer als Wave-Datei ab. Dies geschieht mit der SaveToFile() – Methode, als Parameter wird einfach der Dateiname angegeben.

Beispielprojekt zum Tutorial [98100 Bytes]

Ihre Meinung  

Falls Sie Fragen zu diesem Tutorial haben oder Ihre Erfahrung mit anderen Nutzern austauschen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.

kein directx 8 unter Verweise - Sig 01.12.13 20:25 13 Antworten