AudioIN/OUT Modul (WinAPI)

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
Chimorin
Beiträge: 451
Registriert: 30.01.2013 16:11
Computerausstattung: MSI GTX 660 OC mit TwinFrozr III
6Gb DDR 3 RAM
AMD Phenom II X4 B55 @ 3,6GHz
Windows 7 Home Premium 64-bit

AudioIN/OUT Modul (WinAPI)

Beitrag von Chimorin »

Heyho,

aus einem alten Audiocode, nun von mir neu aufgelegt. Leider funktioniert das Abspielen nicht, weil es sich um Rohdaten handelt (Keine Ahnung, wie man das konvertieren kann).

Wenn sich jemand mit einbringen will, für Mods bin ich offen, bitte hier posten :)

Code: Alles auswählen

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;>>                             >>
;>>  Name: AudioIN              >>
;>>                             >>
;>>  Author: (c) Chimorin       >>
;>>             (Bananenfreak)  >>
;>>                             >>
;>>  Date: 05.10.2014           >>
;>>                             >>
;>>  OS: Windows                >>
;>>                             >>
;>>                             >>
;>>                             >>
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
EnableExplicit


Define.f KeyX, KeyY, MouseX, MouseY
Define nx.f, nz.f, Boost.f = 1, Yaw.f, Pitch.f
Define.i Quit, sound, file
#kam_0 = 0
#window = 0


DeclareModule AudioIn
  EnableExplicit
  
  Structure rec
    buffer.i
    size.i
  EndStructure
  
  #BUFFER_NUM = 8         ; Number of buffer for capture 
  
  Global NewList devices.WAVEINCAPS()
  Global NewList audio.rec()
  Global Dim inHdr.WAVEHDR(#BUFFER_NUM-1)
  Global.i mutex = CreateMutex()
  
  Declare GetDevices()
  Declare Start()
  ;Declare ARead()
EndDeclareModule


Module AudioIn
  Global.i inputZgr       ;Ist das Handle des geöffneten Inputdevice
  
  #SOUND_NCHANNELS = 1    ;Anzahl der Kanäle: 1 = Mono
  #SOUND_NBITS = 16       ;Bittiefe des Audiodings, hier 16bit
  #SOUND_NHERTZ = 44100   ;Hertzanzahl. 
  #BUFFER_SIZE = 512      ; Size of each buffer, should be x2 in Stereo 
  
  
  Declare Error(err.i)
  
  
  Procedure Stop()        ;Stoppt die Aufnahme und freet alles.
  Protected.i i
  
  
  If inputZgr
    Error(waveInReset_(inputZgr))       ;Die Funktion waveInReset stoppt Input auf der vorhandenen Wave-Audio-Eingabegerät und setzt die aktuelle Position auf Null (zurück). Alle ausstehenden Puffer sind  als erledigt markiert und werden zur Anwendung "zurückgeschickt".
    Error(waveInStop_(inputZgr))        ;Die Funktion waveInStop stoppt Wave-Audio input.
    For i = 0 To #BUFFER_NUM - 1 
      Error(waveInUnprepareHeader_(inputZgr, inHdr(i), SizeOf(WAVEHDR)))        ;Diese Funktion bereinigt die Vorbereitung durch waveInPrepareHeader durchgeführt. Die Funktion muss aufgerufen werden, nachdem die Gerätetreiber füllt einen Datenpuffer und gibt sie an die Anwendung. Sie müssen diese Funktion vor der Befreiung der Datenpuffer nennen.
    Next 
    Error(waveInClose_(inputZgr))       ;Diese Funktion schließt die angegebene wave-Audio-Eingabegerät.
  EndIf
  EndProcedure
  
  
  Procedure Error(err.i)        ;Wirft den Fehler aus, der zu diesem Ding gehört.
    Protected.s text
    
    
    If err 
      text = Space(#MAXERRORLENGTH) 
      waveInGetErrorText_(err, text, #MAXERRORLENGTH)       ;Diese Funktion ruft eine Textbeschreibung des von der angegebenen Fehlernummer identifiziert Fehler.
      MessageRequester("Error", text, #MB_ICONERROR)
      Stop() 
      End 
    EndIf 
  EndProcedure
  
  
  Procedure GetDevices()       ;Listet die vorhandenen Audioeingänge auf und gibt den Namen Eines zurück.
    Protected.i size, zaehler
    
    size = waveInGetNumDevs_()
    FirstElement(devices())
    For zaehler = 0 To size -1        ;Die waveInGetNumDevs Funktion gibt die Anzahl der Wellenform-Audio-Eingang im System vorhandenen Geräte.
      AddElement(devices())
      Error(waveInGetDevCaps_(zaehler, devices(), SizeOf(WAVEINCAPS)))       ;Die waveInGetDevCaps Funktion ruft die Fähigkeiten eines gegebenen Wellenform-Audioeingabegerät.
      If Not Bool(devices()\dwFormats & #WAVE_FORMAT_4M16)                   ;dwFormats ist eine Addition aller unterstützter Audioformate (Siehe Anhang 1)
        DeleteElement(devices())
      EndIf
    Next 
  EndProcedure 
  
  
  Procedure ARead(wParam.i)
    Protected.i buffer, size
    
    
    Error(waveInAddBuffer_(inputZgr, wParam, SizeOf(WAVEHDR)))
    ;Debug PeekS(wParam)
    buffer = PeekL(wParam)
    ;Debug buffer
    ;Debug PeekL(buffer)
    ;Debug "000000000000000000"
    
    ;If Not buffer = 0
    ;Debug ".........."
    LockMutex(mutex)
    PushListPosition(audio())
    LastElement(audio())
    AddElement(audio())
    Debug "reingehauen"
    audio()\buffer = buffer;PeekL(buffer)
    audio()\size = PeekL(wParam + 8)
    PopListPosition(audio())
    UnlockMutex(mutex)
      
    ;Else
      ;Debug "Buffer ist 0, warum?"
    ;EndIf
  EndProcedure
  
  
  Procedure CallBack(hWnd.i, uMsg.i, instance.i, wParam.i, lParam.i)        ;Callbackfunktion für alle Inputevents. hWnd: inputZgr | instance ist nur vorhanden, wenn bei waveInOpen_() dwInstance angegeben wurde.
;     Debug hWnd
;     Debug uMsg
;     Debug "i:" + instance
;     Debug "w" + wParam
;     Debug "l:" + lParam
;     Debug "--------------------"
    Select uMsg 
      Case #WIM_DATA
        If hWnd = inputZgr
          ARead(wParam)
          
        Else
          Debug "Da ist was anderes im Busch!"
          
        EndIf
        
      Case #WIM_OPEN
        Debug "Open"
        
      Case #WIM_CLOSE
        Debug "Close"
    EndSelect
  EndProcedure
  
  
  Procedure Start()       ;Initialisierungszeug.
    Protected.WAVEFORMATEX format   ;format.WAVEFORMATEX übergibt in waveInOpen_() Infos über das angeforderte Audiomaterial.
    Protected.i zaehler
    
    
    format\wFormatTag      = 1        ;#WAVE_FORMAT_PCM? Ist es das?
    format\nChannels       = #SOUND_NCHANNELS
    format\wBitsPerSample  = #SOUND_NBITS
    format\nSamplesPerSec  = #SOUND_NHERTZ
    format\nBlockAlign     = #SOUND_NCHANNELS * (#SOUND_NBITS/8)
    format\nAvgBytesPerSec = #SOUND_NHERTZ * format\nBlockAlign
    format\cbSize          = 0
    Error(waveInOpen_(@inputZgr, #WAVE_MAPPER, @format, @CallBack(), 13, #CALLBACK_FUNCTION|#WAVE_FORMAT_DIRECT))        ;Öffnet das spezifische Waveinputdevice.
    
    For zaehler = 0 To #BUFFER_NUM -1
      inHdr(zaehler)\lpData         = AllocateMemory(#BUFFER_SIZE) 
      inHdr(zaehler)\dwBufferLength = #BUFFER_SIZE
      Error(waveInPrepareHeader_(inputZgr, inHdr(zaehler), SizeOf(WAVEHDR)))        ;Diese Funktion erstellt einen Puffer für wave-input. Diese Funktion ermöglicht dem Audiotreiber und dem Betriebssystem (OS) zu Zeit raubende Verarbeitung des Kopf-und / oder Puffer einmal bei der Initialisierung. Die Anwendung kann die Puffer wiederholt ohne zusätzliche Verarbeitungs Nutzung durch den Fahrer oder das Betriebssystem.
      Error(waveInAddBuffer_(inputZgr, inHdr(zaehler), SizeOf(WAVEHDR)))            ;Die waveInAddBuffer Funktion sendet einen inputpuffer, an das gegebene Wave-Audioeingabegerät. Wenn der Puffer voll ist, wird die Anwendung informiert.
    Next zaehler
    
    Error(waveInStart_(inputZgr))        ;Diese Funktion startet input auf dem angegebenen wave-Eingabegerät.
  EndProcedure
EndModule


InitSprite()
InitKeyboard()
InitSound()

OpenWindow(#window, 0, 0, 1800, 1000, "AudioIn-Out Test", #PB_Window_ScreenCentered)
OpenWindowedScreen(WindowID(#window), 10, 10, 2000, 2000, 0, 10, 10, #PB_Screen_SmartSynchronization)

;SetWindowCallback(@CallBack())

AudioIn::GetDevices()
; ForEach AudioIn::devices()
;   Debug PeekS(AudioIn::@devices()\szPname)
; Next
AudioIn::Start()

file = CreateFile(#PB_Any, "D:\test.raw")

Repeat
  Repeat
  Until WindowEvent() = 0
  
  If ExamineKeyboard()
    
  EndIf
  
  If ListSize(AudioIn::audio())
    LockMutex(AudioIn::mutex)
    PushListPosition(AudioIn::audio())
    FirstElement(AudioIn::audio())
    ForEach AudioIn::audio()
      Debug "1 rausgehauen"
      WriteData(file, AudioIn::audio()\buffer, AudioIn::audio()\size)
      DeleteElement(AudioIn::audio())
    Next AudioIn::audio()
    PopListPosition(AudioIn::audio())
    UnlockMutex(AudioIn::mutex)
;     If IsSound(sound)
;       Select SoundStatus(sound)
;         Case #PB_Sound_Stopped
;           Debug "1"
;           FirstElement(AudioIn::audio())
;           ;sound = CatchSound(#PB_Any, AudioIn::audio()\buffer, AudioIn::audio()\size)
;           ;PlaySound(sound)
;           DeleteElement(AudioIn::audio())
;           
;       EndSelect
;       
;     Else
;       Debug "2"
;       FirstElement(AudioIn::audio())
;       Debug "Buffer:" + AudioIn::audio()\buffer
;       sound = CatchSound(#PB_Any, AudioIn::audio()\buffer)
;       Debug sound
;       ;PlaySound(sound)
;       DeleteElement(AudioIn::audio())
;     EndIf
  EndIf
  
  
  FlipBuffers()
Until KeyboardPushed(#PB_Key_Escape) Or Quit = 1

CloseFile(file)
End
Wie man hier unten drunter nachlesen kann, gibt es eine überarbeitete Version von Dadido3 mit mehr Funktionen:
>>> Dadido3 GitHub Releases <<<
Zuletzt geändert von Chimorin am 17.10.2014 15:58, insgesamt 1-mal geändert.
Bild

- formerly known as Bananenfreak -
Benutzeravatar
Dadido3
Beiträge: 103
Registriert: 24.02.2007 22:15
Kontaktdaten:

Re: AudioIN Modul (WINAPI)

Beitrag von Dadido3 »

Guten Tag,

Vielen Dank für den Code.
Soetwas habe ich schon lange gesucht und gebraucht.
Und als Modul ist das natürlich noch viel einfacher verwendbarer als so mancher alter Audioaufnahme-code, welcher hier und da rumfliegt.

Ideen und Verbesserungsvorschläge:
  • Mehr Funktionen zur einfachereren Verwendung. Im Moment muss man ja die Audiodaten manuell aus der Liste auslesen (Und dazu den Mutex sperren und entsperren). Eine einfache Funktion, welche den nächsten verfügbaren Block einfach an eine angegebene Speicheradresse schreibt, tut einiges zur Übersicht beitragen, und hilft das Modul mehr zu kapseln. Des Weiteren wäre es hilfreich wenn es eine Funktion zur Initalisation einer Audioschnittstelle geben würde, bei der man Abtastrate, Blockgröße, Kanalanzahl usw. übergeben kann. Eventuell in der Form umgesetzt, dass es möglich ist mehrere Audiogeräte gleichzeitig zu verwenden.
  • Hier mal ein Beispiel wie man das Modul verwenden könnte, wenn man die Funktionen ein wenig aufräumt:

    Code: Alles auswählen

    AudioIn::GetDevices()
    
    ForEach AudioIn::devices()
      Debug PeekS(AudioIn::@devices()\szPname)
    Next
    
    ; #### Initialize a device
    AudioIn_ID = AudioIn::Initialize(Device, Channels, BitPerSample, Samplerate)
    
    Buffer_Size = 1234
    *Buffer = AllocateMemory(Buffer_Size)
    
    ; #### Start recording
    AudioIn::Start(AudioIn_ID)
    
    Repeat
      ; #### Check if there is enough available data, read that data
      if AudioIn::Available_Data(AudioIn_ID) >= Buffer_Size
        AudioIn::Read_Data(AudioIn_ID, *Buffer, Buffer_Size)
      endif
      ; #### Do something with that data
    Until Quit
    
    ; #### Stop recording
    AudioIn::Stop(AudioIn_ID)
    
    ; #### Free the device
    AudioIn::Deinitialize(AudioIn_ID)
    Das ist natürlich nur ein Serviervorschlag ;)
  • Da ja mit den wave***_() funktionen auch eine Audioausgabe auf eine ähnliche Art und Weise realisierbar ist, wäre es schön wenn dieses Modul dahingehend erweiter würde.
  • In der Funktion ARead() gibt es ein paar sachen die geändert werden müssen. Z.B. ist es nicht optimal PeekL zum Auslesen der Speicheradresse zu verwenden. Hab mal ein wenig aufgeräumt und korrigiert:

    Code: Alles auswählen

    Procedure ARead(*WaveHDR.WAVEHDR)
      LockMutex(mutex)
      
      LastElement(audio())
      AddElement(audio())
      
      ; #### Nicht vergessen den Buffer bei DeleteElement(...) freizugeben.
      audio()\buffer = AllocateMemory(*WaveHDR\dwBufferLength)
      CopyMemory(*WaveHDR\lpData, audio()\buffer, *WaveHDR\dwBufferLength)
      
      audio()\size = *WaveHDR\dwBufferLength
      
      Error(waveInAddBuffer_(inputZgr, *WaveHDR, SizeOf(WAVEHDR)))
      
      UnlockMutex(mutex)
    EndProcedure
Zuletzt geändert von Dadido3 am 13.10.2014 01:33, insgesamt 1-mal geändert.
Benutzeravatar
Chimorin
Beiträge: 451
Registriert: 30.01.2013 16:11
Computerausstattung: MSI GTX 660 OC mit TwinFrozr III
6Gb DDR 3 RAM
AMD Phenom II X4 B55 @ 3,6GHz
Windows 7 Home Premium 64-bit

Re: AudioIN Modul (WINAPI)

Beitrag von Chimorin »

Heyho,
Danke für deine Antwort und schön, dass dir der Code gefällt. Wie erwähnt, sind einige Teile aus einem alten Code übernommen worden. Genauer gesagt die Sachen, die ich nicht ganz verstanden habe und aber wichtig waren (Die Sache mit dem PeekL()).
Der Code funktioniert so und das wollte ich zeigen. Wenn ich Zeit (und Lust) habe, werde ich das erweitern, deine Ideen sind super!

Ich kenne mich da mit DLLs nicht so aus, aber kann man das Ganze auch in eine DLL packen? Das würde ja super aussehen :-)

Noch eine Anmerkung: ARead() wird so nicht funktionieren. wParam ist das Audiohandle, lParam die Speicheradresse der Daten. Den Buffer muss man wieder zurückschicken, sonst kommt nach 8Buffern nichts mehr Neues nach. CopyMemory() wird dennoch implementiert :-)
Bild

- formerly known as Bananenfreak -
Benutzeravatar
Dadido3
Beiträge: 103
Registriert: 24.02.2007 22:15
Kontaktdaten:

Re: AudioIN Modul (WINAPI)

Beitrag von Dadido3 »

Abend,

Da ich den Code eventuell bald brauche hab ich ihn mal ein wenig aufgeräumt und benutzbarer gemacht.
Es ist nun möglich verschiedene Geräte gleichzeitig zu verwenden.
Des Weiteren sind mehr einstellungsmöglichkeiten vorhanden.

Möglicherweise werde ich später noch ein Modul für die Audioausgabe basteln, mal sehen.

Hier mal eine Übersicht über die verfügbaren Methoden
  • AudioIn::GetError()
    Gibt den letzten Fehler als Text zurück.
  • AudioIn::GetDevices()
    Aktualisiert die Geräteliste ( AudioIn::Device() )
  • AudioIn::Initialize(DeviceID.i, Samplerate.i, Channels.i, Bits.i, *CallBack, Buffer_Blocksize.i=512, Buffer_Blocks.i=8)
    Initialisiert einen Audio-Eingang mit gegebenen Parametern. Rückgabe ist das "Objekt", oder #NULL falls ein Fehler auftrat.
  • AudioIn::Deinitialize(*AudioIn)
    Deinitialisiert das "Objekt". Eine Rückgabe von #False bedeutet Fehler.
  • AudioIn::Start(*AudioIn)
    Startet die Aufnahme. Daten werden fortan gepuffert und können mit Read_Data(...) abgerufen werden. Eine Rückgabe von #False bedeutet Fehler.
  • AudioIn::Stop(*AudioIn)
    Stoppt die Aufnahme. Eine Rückgabe von #False bedeutet Fehler.
  • AudioIn::Available_Data(*AudioIn)
    Gibt die aktuelle Datenmenge im Puffer zurück. Rückgabe von -1 bedeutet Fehler.
  • AudioIn::Read_Data(*AudioIn, *Destination, Amount.i)
    Liest die nächsten verfügbaren Daten aus, und schreibt sie in *Destination. Amount gibt das Maximum an Daten in Bytes an. Die wahre Menge an gelesenen Bytes wird zurückgegeben. Eine Rückgabe von -1 bedeutet ein Fehler.
Hier das Include:

Code: Alles auswählen

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;>>                             >>
;>>  Name: AudioIN              >>
;>>                             >>
;>>  Author: (c) Chimorin       >>
;>>             (Bananenfreak)  >>
;>>                             >>
;>>  Extended and refactored    >>
;>>       by David Vogel        >>
;>>     (Dadido3, Xaardas)      >>
;>>                             >>
;>>  Date: 13.10.2014           >>
;>>                             >>
;>>  OS: Windows                >>
;>>                             >>
;>>                             >>
;>>                             >>
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

DeclareModule AudioIn
  EnableExplicit
  
  Global NewList Device.WAVEINCAPS()
  
  ; #### Methods
  Declare.s GetError()                                  ; Returns the error-message of the last error
  Declare   GetDevices()                                ; Refreshes the list of devices ( AudioIn::Device() )
  Declare   Initialize(DeviceID.i, Samplerate.i, Channels.i, Bits.i, *CallBack, Buffer_Blocksize.i=512, Buffer_Blocks.i=8) ; Initializes a new input. It returns a handle to the instance
  Declare   Deinitialize(*AudioIn)                      ; Deinitializes the instance
  Declare   Start(*AudioIn)                             ; Starts recording
  Declare   Stop(*AudioIn)                              ; Stops recording
  Declare   Available_Data(*AudioIn)                    ; Returns the amount of available data
  Declare   Read_Data(*AudioIn, *Destination, Amount.i) ; Reads a specific amount of data and writes it at the given *Destination. The result of the function is the actual amount of read data.
  
EndDeclareModule


Module AudioIn
  
  Prototype   External_CallBack(*AudioIn)
  
  Structure AudioIn_Block
    *Buffer
    Size.i
    
    Position.i
  EndStructure
  
  Structure AudioIn
    uDeviceID.i
    hwi.i
    
    Mutex.i
    Semaphore.i
    
    List inHdr.WAVEHDR()
    
    List Block.AudioIn_Block()
    Available_Data.i
    
    External_CallBack.External_CallBack
  EndStructure
  
  Global Last_Error.i
  
  Procedure.s GetError() ; Returns the last error-message.
    Protected Text.s
    
    Text = Space(#MAXERRORLENGTH)
    waveInGetErrorText_(Last_Error, Text, #MAXERRORLENGTH)
    
    ProcedureReturn Text
  EndProcedure
  
  Procedure Error(mmrError.i)
    Last_Error = mmrError
    
    If mmrError
      ProcedureReturn #True
    Else
      ProcedureReturn #False
    EndIf
  EndProcedure
  
  Procedure GetDevices() ; Lists all the available input-devices
    Protected.i i, Devices
    
    ; #### Get amount of available input-devices
    Devices = waveInGetNumDevs_()
    
    ClearList(Device())
    
    For i = 0 To Devices - 1
      AddElement(Device())
      waveInGetDevCaps_(i, Device(), SizeOf(WAVEINCAPS)) ;Die waveInGetDevCaps Funktion ruft die Fähigkeiten eines gegebenen Wellenform-Audioeingabegerät.
    Next
    
  EndProcedure
  
  Procedure CallBack(*hwi, uMsg.i, *AudioIn.AudioIn, *dwParam1, *dwParam2)
    Protected *wvhdr.WAVEHDR
    
    Select uMsg
      Case #WIM_DATA ; #### A new block of data is available, add it to the *AudioIn\Block() queue.
        *wvhdr = *dwParam1
        
        LockMutex(*AudioIn\Mutex)
        
        If *wvhdr\lpData And *wvhdr\dwBufferLength > 0
          LastElement(*AudioIn\Block())
          AddElement(*AudioIn\Block())
          *AudioIn\Block()\Buffer = AllocateMemory(*wvhdr\dwBufferLength)
          *AudioIn\Block()\Size = *wvhdr\dwBufferLength
          CopyMemory(*wvhdr\lpData, *AudioIn\Block()\Buffer, *wvhdr\dwBufferLength)
          
          *AudioIn\Available_Data + *wvhdr\dwBufferLength
        EndIf
        
        ;SignalSemaphore(*AudioIn\Semaphore)
        
        UnlockMutex(*AudioIn\Mutex)
        
        waveInAddBuffer_(*hwi, *wvhdr, SizeOf(WAVEHDR))
        
        If *AudioIn\External_CallBack
          *AudioIn\External_CallBack(*AudioIn)
        EndIf
        
      Case #WIM_OPEN
        
      Case #WIM_CLOSE
        
    EndSelect
  EndProcedure
  
  Procedure Initialize(DeviceID.i, Samplerate.i, Channels.i, Bits.i, *CallBack, Buffer_Blocksize.i=512, Buffer_Blocks.i=8) ; To use the windows wavemapper set uDeviceID = #WAVE_MAPPER
    Protected wfx.WAVEFORMATEX ; wfx.WAVEFORMATEX identifies the desired format for recording waveform-audio data.
    Protected *AudioIn.AudioIn = AllocateStructure(AudioIn)
    Protected i
    
    wfx\wFormatTag      = #WAVE_FORMAT_PCM
    wfx\nChannels       = Channels
    wfx\wBitsPerSample  = Bits
    wfx\nSamplesPerSec  = Samplerate
    wfx\nBlockAlign     = Channels * (Bits/8)
    wfx\nAvgBytesPerSec = Samplerate * wfx\nBlockAlign
    wfx\cbSize          = 0
    
    If Error( waveInOpen_(@*AudioIn\hwi, DeviceID, wfx, @CallBack(), *AudioIn, #CALLBACK_FUNCTION | #WAVE_FORMAT_DIRECT) ) ; The waveInOpen function opens the given waveform-audio input device for recording.
      FreeStructure(*AudioIn)
      ProcedureReturn #Null
    EndIf
    
    ; #### Create a specific amount of buffers
    For i = 1 To Buffer_Blocks
      AddElement(*AudioIn\inHdr())
      
      *AudioIn\inHdr()\lpData = AllocateMemory(Buffer_Blocksize)
      *AudioIn\inHdr()\dwBufferLength = Buffer_Blocksize
      
      waveInPrepareHeader_(*AudioIn\hwi, *AudioIn\inHdr(), SizeOf(WAVEHDR))        ;Diese Funktion erstellt einen Puffer für wave-input. Diese Funktion ermöglicht dem Audiotreiber und dem Betriebssystem (OS) zu Zeit raubende Verarbeitung des Kopf-und / oder Puffer einmal bei der Initialisierung. Die Anwendung kann die Puffer wiederholt ohne zusätzliche Verarbeitungs Nutzung durch den Fahrer oder das Betriebssystem.
      waveInAddBuffer_(*AudioIn\hwi, *AudioIn\inHdr(), SizeOf(WAVEHDR))            ;Die waveInAddBuffer Funktion sendet einen inputpuffer, an das gegebene Wave-Audioeingabegerät. Wenn der Puffer voll ist, wird die Anwendung informiert.
    Next
    
    *AudioIn\Mutex = CreateMutex()
    *AudioIn\Semaphore = CreateSemaphore()
    
    *AudioIn\External_CallBack = *CallBack
    
    ProcedureReturn *AudioIn
  EndProcedure
  
  Procedure Deinitialize(*AudioIn.AudioIn)  ; Deinitialize the instance.
    If Not *AudioIn
      ProcedureReturn #False
    EndIf
    
    ;waveInReset_(*AudioIn\hwi)       ;Die Funktion waveInReset stoppt Input auf der vorhandenen Wave-Audio-Eingabegerät und setzt die aktuelle Position auf Null (zurück). Alle ausstehenden Puffer sind  als erledigt markiert und werden zur Anwendung "zurückgeschickt".
    waveInStop_(*AudioIn\hwi)        ;Die Funktion waveInStop stoppt Wave-Audio input.
    
    ForEach *AudioIn\inHdr()
      waveInUnprepareHeader_(*AudioIn\hwi, *AudioIn\inHdr(), SizeOf(WAVEHDR))        ;Diese Funktion bereinigt die Vorbereitung durch waveInPrepareHeader durchgeführt. Die Funktion muss aufgerufen werden, nachdem die Gerätetreiber füllt einen Datenpuffer und gibt sie an die Anwendung. Sie müssen diese Funktion vor der Befreiung der Datenpuffer nennen.
      FreeMemory(*AudioIn\inHdr()\lpData)
    Next
    
    waveInClose_(*AudioIn\hwi)       ;Diese Funktion schließt die angegebene wave-Audio-Eingabegerät.
    
    ForEach *AudioIn\Block()
      FreeMemory(*AudioIn\Block()\Buffer)
    Next
    
    FreeMutex(*AudioIn\Mutex)
    FreeSemaphore(*AudioIn\Semaphore)
    
    FreeStructure(*AudioIn)
    
    ProcedureReturn #True
  EndProcedure
  
  Procedure Start(*AudioIn.AudioIn)
    If Not *AudioIn
      ProcedureReturn #False
    EndIf
    
    If Error( waveInStart_(*AudioIn\hwi) )
      ProcedureReturn #False
    EndIf
    
    ProcedureReturn #True
  EndProcedure
  
  Procedure Stop(*AudioIn.AudioIn)
    If Not *AudioIn
      ProcedureReturn #False
    EndIf
    
    If Error( waveInStop_(*AudioIn\hwi) )
      ProcedureReturn #False
    EndIf
    
    ProcedureReturn #True
  EndProcedure
  
  Procedure Available_Data(*AudioIn.AudioIn)
    Protected Available_Data.i
    
    If Not *AudioIn
      ProcedureReturn -1
    EndIf
    
    LockMutex(*AudioIn\Mutex)
    Available_Data = *AudioIn\Available_Data
    UnlockMutex(*AudioIn\Mutex)
    
    ProcedureReturn Available_Data
  EndProcedure
  
  Procedure Read_Data(*AudioIn.AudioIn, *Destination, Amount.i)
    Protected Bytes_Read.i
    Protected Temp_Size.i
    
    If Not *AudioIn
      ProcedureReturn -1
    EndIf
    
    ;If TrySemaphore(*AudioIn\Semaphore)
      LockMutex(*AudioIn\Mutex)
      
      While (Amount > 0 And FirstElement(*AudioIn\Block()))
        Temp_Size = *AudioIn\Block()\Size - *AudioIn\Block()\Position
        If Temp_Size > Amount
          Temp_Size = Amount
        EndIf
        
        CopyMemory(*AudioIn\Block()\Buffer + *AudioIn\Block()\Position, *Destination, Temp_Size)
        
        *AudioIn\Block()\Position + Temp_Size
        If *AudioIn\Block()\Position >= *AudioIn\Block()\Size
          FreeMemory(*AudioIn\Block()\Buffer)
          DeleteElement(*AudioIn\Block())
        EndIf
        
        Bytes_Read + Temp_Size
        *Destination + Temp_Size
        Amount - Temp_Size
        *AudioIn\Available_Data - Temp_Size
      Wend
      
      UnlockMutex(*AudioIn\Mutex)
    ;EndIf
    
    ProcedureReturn Bytes_Read
  EndProcedure
  
EndModule

Und ein Beispiel (Muss als Thread-Safe kompiliert werden):

Code: Alles auswählen

EnableExplicit

; ##################################################### Includes ####################################################

XIncludeFile "Includes/AudioIn.pbi"

; ##################################################### Prototypes ##################################################

; ##################################################### Structures ##################################################

; ##################################################### Constants ###################################################

Enumeration #PB_Event_FirstCustomValue
  #CustomEvent_Redraw
EndEnumeration

; ##################################################### Structures ##################################################

Structure Main
  *AudioIn
  
  Quit.i
EndStructure
Global Main.Main

Structure Main_Window
  ID.i
  
  Canvas.i
EndStructure
Global Main_Window.Main_Window

; ##################################################### Variables ###################################################

; ##################################################### Procedures ##################################################

Procedure Main_Window_Open()
  Main_Window\ID = OpenWindow(#PB_Any, 0, 0, 1000, 400, "AudioIn Example", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_ScreenCentered | #PB_Window_SizeGadget)
  
  If Main_Window\ID
    
    Main_Window\Canvas = CanvasGadget(#PB_Any, 0, 0, 1000, 400)
    
    ;AddWindowTimer(Main_Window\ID, 0, 10)
    
  EndIf
EndProcedure

Procedure Main_Window_Redraw()
  Protected Width = GadgetWidth(Main_Window\Canvas)
  Protected Height = GadgetHeight(Main_Window\Canvas)
  
  Protected Need = Width*2
  Protected Available = AudioIn::Available_Data(Main\AudioIn)
  
  If Available < Need
    ProcedureReturn
  EndIf
  
  Protected *Temp = AllocateMemory(Need)
  Protected *Pointer.Word
  
  Protected Y.f, Old_Y.f
  
  Protected i
  
  AudioIn::Read_Data(Main\AudioIn, *Temp, Need)
  
  If StartDrawing(CanvasOutput(Main_Window\Canvas))
    
    Box(0, 0, Width, Height, #White)
    
    DrawingMode(#PB_2DDrawing_Transparent)
    
    DrawText(0, 0, "Queue-Size: "+Str(AudioIn::Available_Data(Main\AudioIn))+"B", 0)
    
    *Pointer = *Temp
    For i = 0 To Width-1
      Y = Height/2 - *Pointer\w*Height/65536.0
      LineXY(i-1, Old_Y, i, Y, 0)
      Old_Y = Y
      *Pointer + 2
    Next
    
    StopDrawing()
  EndIf
  
  FreeMemory(*Temp)
EndProcedure

Procedure Notifier_CallBack(*AudioIn)
  PostEvent(#CustomEvent_Redraw)
EndProcedure

; ##################################################### Initialisation ##############################################

Main_Window_Open()

AudioIn::GetDevices()

;ForEach AudioIn::Device()
;  Debug AudioIn::Device()\szPname
;Next

Main\AudioIn = AudioIn::Initialize(0, 44100, 2, 16, @Notifier_CallBack())

AudioIn::Start(Main\AudioIn)

; ##################################################### Main ########################################################

Define Counter

Repeat
  
  Repeat
    Select WaitWindowEvent(100)
      Case #PB_Event_Gadget
        
      Case #CustomEvent_Redraw
        Main_Window_Redraw()
        
      Case #PB_Event_SizeWindow
        ResizeGadget(Main_Window\Canvas, 0, 0, WindowWidth(EventWindow()), WindowHeight(EventWindow()))
        
      Case #PB_Event_CloseWindow
        Main\Quit = #True
        AudioIn::Stop(Main\AudioIn)
        
      Case 0
        Break
    EndSelect
  ForEver
  
Until Main\Quit

; ##################################################### End #########################################################

AudioIn::Deinitialize(Main\AudioIn)
Benutzeravatar
Dadido3
Beiträge: 103
Registriert: 24.02.2007 22:15
Kontaktdaten:

Re: AudioIN Modul (WINAPI)

Beitrag von Dadido3 »

Moin,

Hab soeben noch ein passendes AudioOut-Modul geschrieben.
Dazu gibts noch 2 weitere Beispiele:
  • Sinusausgabe mit änderbarer Frequenz und Amplitude.
  • Live Wiedergabe eines Audioeingangs. (Also alles was am Mikrofon reinkommt, wird direkt ausgegeben.)
Übersichtshalber habe ich alles mal auf GitHub geladen und unter MIT Lizenz gestellt, falls das ok geht.
>>> GitHub Releases <<<
Entweder kannst du den deinen Thread anpassen, oder ich mache einen neuen auf. Je nachdem wie es dir am besten passt.
Benutzeravatar
Chimorin
Beiträge: 451
Registriert: 30.01.2013 16:11
Computerausstattung: MSI GTX 660 OC mit TwinFrozr III
6Gb DDR 3 RAM
AMD Phenom II X4 B55 @ 3,6GHz
Windows 7 Home Premium 64-bit

Re: AudioIN Modul (WINAPI)

Beitrag von Chimorin »

Das sieht ja super aus.
Ich werde es auf jeden Fall morgen früh testen.
MIT Lizenz? Finde ich gut :)

Ich kann den Namen anpassen, wie soll der Name lauten?
Bild

- formerly known as Bananenfreak -
Andreas21
Beiträge: 390
Registriert: 30.08.2004 09:05
Computerausstattung: Desktop
Windows 10 Pro x64
CPU: AMD Ryzen 5 2600 3.40 GHz
Ram: 16GB RAM
Grafik: NVIDA Geforce 1060
PB: 5.72 X86/X64
Wohnort: Heidelberg

Re: AudioIN/OUT Modul (WinAPI)

Beitrag von Andreas21 »

Datei: Example_Sine.pb

in der X86 und X64 Version von PB 5.31

Code: Alles auswählen

Procedure Notifier_CallBack(*AudioOut)
  Protected *Temp, Temp_Size.i
  Static Rotation.d
  
  While AudioOut::GetQueuedBlocks(*AudioOut) <= 3
    
    Temp_Size = AudioOut::GetBufferBlocksize(*AudioOut)
    *Temp = AllocateMemory(Temp_Size) ;<- Temp_Size = -1
    
    Define Left.d, Right.d, i
    For i = 0 To Temp_Size / 4 - 1
      Left = Sin(Rotation) * Amplitude
      Right = Sin(Rotation) * Amplitude
      
      PokeW(*Temp + i*4    , Left*32767)
      PokeW(*Temp + i*4 + 2, Right*32767)
      
      Rotation + 2.0*#PI / #Samplerate * Frequency
    Next
    
    AudioOut::Write_Data(Main\AudioOut, *Temp, Temp_Size)
    
    FreeMemory(*Temp)
    
  Wend
EndProcedure
Temp_Size ist in Zeile 59 = - 1
Es fehlt eine Fehler abfrage.
Windows 10 x64 Pro - PB 5.61 X64 / x32 - PB 4.6 x32
Benutzeravatar
Dadido3
Beiträge: 103
Registriert: 24.02.2007 22:15
Kontaktdaten:

Re: AudioIN/OUT Modul (WinAPI)

Beitrag von Dadido3 »

Mahlzeit,

Den Fehler hatte ich schon länger behoben, aber noch nicht released.

Hauptsächlich bestand der Fehler darin, dass device 6 gewählt wurde anstatt den #WAVE_MAPPER.
Ich hatte es lediglich vergessen dies, nach tests am code, wieder zurück zu ändern.

Des Weiteren habe ich noch ein kleines und einfaches Beispiel zum Abspielen ohne Callback gemacht, und ein paar andere kleinigkeiten verbessert.
Falls irgendetwas nicht wie erwartet funktioniert, bitte bescheid sagen.

Das Update is wie immer auf GitHub.


Zuletzt als neu markiert von Chimorin am 08.12.2014 22:29.
Antworten