Bilgus Posted February 16, 2019 Author Share Posted February 16, 2019 (edited) Thanks for testing, I'll update the UDF https://docs.microsoft.com/en-us/windows/desktop/api/Audioclient/nf-audioclient-iaudioclient-initialize On Windows versions prior to Windows 10, a pull-mode capture client will not receive any events when a stream is initialized with event-driven buffering (AUDCLNT_STREAMFLAGS_EVENTCALLBACK) and is loopback-enabled (AUDCLNT_STREAMFLAGS_LOOPBACK). If the stream is opened with this configuration, the Initialize call succeeds, but relevant events are not raised to notify the capture client each time a buffer becomes ready for processing. To work around this, initialize a render stream in event-driven mode. Each time the client receives an event for the render stream, it must signal the capture client to run the capture thread that reads the next set of samples from the capture endpoint buffer. As of Windows 10 the relevant event handles are now set for loopback-enabled streams that are active. Note that all streams must be opened in share mode because exclusive-mode streams cannot operate in loopback mode. For more information about audio loopback, see Loopback Recording. LIES! Edited February 16, 2019 by Bilgus argumentum 1 Link to comment Share on other sites More sharing options...
Nine Posted February 16, 2019 Share Posted February 16, 2019 Tested on a very long song 25.5 mins. So far so good...win7 Bilgus 1 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Screen Scraping Multi-Threading Made Easy Link to comment Share on other sites More sharing options...
Bilgus Posted February 16, 2019 Author Share Posted February 16, 2019 It writes directly to disk so length should only depend on harddisk space I found with the data in memory at very high bitrates as the memory filled up it started gathering a lot of glitches. An Improvement might be a small buffer to decrease disk writes but I didn't bother for the example Thanks for testing Link to comment Share on other sites More sharing options...
Bilgus Posted February 17, 2019 Author Share Posted February 17, 2019 I've finally found a decent spec for the Wave Extensible Format: http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html turns out that Windows Media Player needs a 'fact' field added for Wave Extensible Formats In light of the new info I've added a new function to the udf _WASAPI_RIFF_Header this function replaces the previous wave header function in the example script (WavHeader_Init) that I've left it commented out in the example script for now just in case an issue arises. The generated WAV files do play in VLC and Windows Media Player so are probably fine. I've also added padding to the WavSaver per the above spec but not sure how important it really is... Bilgus argumentum 1 Link to comment Share on other sites More sharing options...
argumentum Posted February 17, 2019 Share Posted February 17, 2019 flawless Bilgus 1 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting. Link to comment Share on other sites More sharing options...
argumentum Posted February 17, 2019 Share Posted February 17, 2019 added a "pause" ( since is there to code ) to the example Spoiler expandcollapse popup;Bilgus 2019 ; https://www.autoitscript.com/forum/topic/197769-wasapi-audio-capture-w-loopback-udf/ #include 'WasApi_capture.au3' #include <Misc.au3>;_IsPressed #include <WinAPIFiles.au3> Global Const $sTmpFile = @ScriptDir & "\wavecap.raw" Global $g_hUser32 = DllOpen("user32.dll") OnAutoItExitRegister("_Exit") ListAvailableEndpoints() Global $g_sID_Endpoint = _WASAPI_GetDefaultAudioEndpoint($eRender) Global $aEpInfo = _WASAPI_EndpointInfo($g_sID_Endpoint) ConsoleWrite("Capturing From: " & $aEpInfo[$eWASInfo_Name] & @CRLF & "ESC key ends capture" & @CRLF) ListDeviceProperties($g_sID_Endpoint) _Main($g_sID_Endpoint) Func _Exit() DllClose($g_hUser32) EndFunc ;==>_Exit Func _Main($sDeviceID) Local $hFile = _WinAPI_CreateFileEx($sTmpFile, $CREATE_ALWAYS, $GENERIC_WRITE, 0, $FILE_ATTRIBUTE_TEMPORARY) Local $aSC = _WASAPI_Stream_Open($sDeviceID, 500) ;Setup stream capture from default render device with a 500Ms buffer ConsoleWrite("Buffer Sz: " & _WASAPI_Stream_BufferSizeBytes($aSC) & " Bytes" & @CRLF) Local $hEvent = _WASAPI_Stream_CaptureEventHandle($aSC) Local $pWFX = _WASAPI_Stream_WaveFormat($aSC) Local $nBlockAlign = _WFx_Get($pWFX, "nBlockAlign") Local $iAvgBytesPerSec = _WFx_Get($pWFX, "nAvgBytesPerSec") Local $tWave = _WASAPI_RIFF_Header($aSC) ;;WavHeader_Init($pWFX) Local $iBytesWritten = 0, $iGlitchCount = 0 Local $nBytes, $iFlags Local $iRes = 0 Local $iSilence = 10 * 15 ;15 seconds of silence before quit _WinAPI_WriteFile($hFile, DllStructGetPtr($tWave), DllStructGetSize($tWave), $nBytes) Local $iPaused = 0, $oICaptureClient = _WASAPI_Stream_StartCapture($aSC) If @error Then ConsoleWrite("Unable to start Capture") Return EndIf While $iSilence > 0 And Not _IsPressed("1B", $g_hUser32) $iRes = _WinAPI_WaitForSingleObject($hEvent, 100) If $iRes = 0 Then $iFlags = CapFunc($oICaptureClient, $hFile, $nBlockAlign, $iBytesWritten, $iGlitchCount) $iPaused = @extended If $iFlags Then If BitAND($iFlags, $AUDCLNT_BUFFERFLAGS_SILENT) Then ConsoleWrite("Paused ( waiting for sound play ) - " & $iSilence & @CRLF) ; "Capturing Silence" & @CRLF) $iSilence -= 1 EndIf If BitAND($iFlags, $AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR) Then ConsoleWrite("Time Stamp Error" & @CRLF) EndIf If $iGlitchCount > 0 Then ConsoleWrite("Audio Glitch Detected" & @CRLF) $iGlitchCount = 0 EndIf EndIf ;ConsoleWrite($iBytesWritten & @crlf) Sleep(100) ElseIf $iRes = $WASAPI_EVENT_WAIT_TIMEOUT Then ConsoleWrite("Timeout" & @CRLF) Else ConsoleWrite("Error Waiting for event" & @CRLF) ExitLoop EndIf If $iPaused Then ToolTip("Paused ( waiting for sound play ) - " & $iSilence) Else ToolTip(($iBytesWritten / $iAvgBytesPerSec) & "s") EndIf WEnd ConsoleWrite("Exit or Silence" & @CRLF) ToolTip("") _WASAPI_Stream_StopCapture($aSC) _WinAPI_FlushFileBuffers($hFile) _WinAPI_CloseHandle($hFile) _WASAPI_Stream_Close($aSC) ; Local $hFileOpen = _WinAPI_CreateFileEx($sTmpFile, $OPEN_EXISTING, $GENERIC_WRITE, 0) If $hFileOpen = -1 Then MsgBox($MB_ICONERROR, @ScriptName & " Error", "An error occurred when opening the file: " & $sTmpFile) Exit EndIf WavSaver($hFileOpen) ;Write the header data to beginning of capture file _WinAPI_CloseHandle($hFileOpen) Local $sOutfile = @ScriptDir & "\wavecap" & @YDAY & "_" & @HOUR & @MIN & @SEC & ".wav" If FileMove($sTmpFile, $sOutfile) Then ConsoleWrite($sOutfile & " - " & FileGetSize($sOutfile) & " bytes" & @CRLF) Else MsgBox($MB_ICONERROR, @ScriptName & " Error", "Error renaming tmpfile " & $sTmpFile) EndIf EndFunc ;==>_Main Func CapFunc($oICaptureClient, $hFile, $nBlockAlign, ByRef $iBytesWritten, ByRef $iGlitchCount) ;Capture function for the stream, saves data directly to open file ;returns flags from the audioclient ;$AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY, ;$AUDCLNT_BUFFERFLAGS_SILENT ;$AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR ; ;If $AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY occurs $iGlitchCount is incremented and packet is skipped ;$iBytesWritten is incremented by number of bytes written Local $iPacketLength = 0, $iNumFramesAvailable = 0, $iFlags = 0 Local $iFlags_Session = 0 ;Flags for any packet during this capture session Local $pData, $nBytes, $iPaused = 0 ;Check if there are any packets to retrieve While (Not $oICaptureClient.GetNextPacketSize($iPacketLength)) And $iPacketLength <> 0 $iFlags = 0 ;Get the available data in the shared buffer. $oICaptureClient.GetBuffer($pData, $iNumFramesAvailable, $iFlags, 0, 0) $iFlags_Session = BitOR($iFlags_Session, $iFlags) If $iFlags = $AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY And $iBytesWritten <> 0 Then $iGlitchCount += 1 ElseIf BitAND($iFlags, $AUDCLNT_BUFFERFLAGS_SILENT) Then $iPaused = 1 Else _WinAPI_WriteFile($hFile, $pData, $iNumFramesAvailable * $nBlockAlign, $nBytes) $iBytesWritten += $nBytes EndIf $oICaptureClient.ReleaseBuffer($iNumFramesAvailable) ;We are done with the buffer release it back to client WEnd Return SetError(0, $iPaused, $iFlags_Session) EndFunc ;==>CapFunc #Region ### WAVE ### Func WavSaver($hFileOpen) ;Updates FileSize, DATALen, dwSampleLength(if applicable) fields in the wav header Local $tWaveHeader = _WASAPI_RIFF_Header(0) ;;WavHeader_Init(0) Local $iWaveHeaderLen = DllStructGetSize($tWaveHeader) Local $iFileSz = _WinAPI_GetFileSizeEx($hFileOpen) Local $nBytes If BitAND($iFileSz, 1) Then ;Padding byte needed Local $tPad = DllStructCreate("byte pad") _WinAPI_SetFilePointer($hFileOpen, 0, 2) ;write padding byte at eof _WinAPI_WriteFile($hFileOpen, DllStructGetPtr($tPad), 1, $nBytes) $iFileSz += $nBytes ConsoleWrite("Adding padding byte" & @CRLF) EndIf DllStructSetData($tWaveHeader, "FileSize", $iFileSz - 8) Local $iDataLen = $iFileSz - $iWaveHeaderLen DllStructSetData($tWaveHeader, "DATALen", $iDataLen) Local $wBitsPerSample = DllStructGetData($tWaveHeader, "wBitsPerSample") DllStructSetData($tWaveHeader, "dwSampleLength", $iDataLen / ($wBitsPerSample / 8)) _WinAPI_SetFilePointer($hFileOpen, 0, 0) ;Rewrite the wavheader at the beginning of the file _WinAPI_WriteFile($hFileOpen, DllStructGetPtr($tWaveHeader), $iWaveHeaderLen, $nBytes) If $nBytes <> $iWaveHeaderLen Then MsgBox($MB_ICONERROR, @ScriptName & " Error", "Error updating wav header") Return $iFileSz EndFunc ;==>WavSaver #cs ;See -> _WASAPI_RIFF_Header Func WavHeader_Init($pWFX, $bReinit = False) ;Initializes the wav header to the stream format passed in $pWFX ;set $bReinit = True if you change the format as its only created once Local Const $WAVE_FORMAT_PCM = 1 Local Const $tagDSWAVEHEADER = "struct; char RIFF[4]; uint FileSize; char WAVE[4]; char FMT[4]; uint FMTLen; " & _ "word Format; word Channels; uint SampleRate; uint BytesPerSec; word BlockAlign; " & _ "word BitsPerSample; char DATA[4]; uint DATALen; endstruct;" Static Local $tWave = DllStructCreate($tagDSWAVEHEADER) Static Local $pWFX_Last = 0 If $bReinit Or ($pWFX <> 0 And $pWFX <> $pWFX_Last) Then $pWFX_Last = $pWFX DllStructSetData($tWave, "RIFF", "RIFF") DllStructSetData($tWave, "FileSize", 0) DllStructSetData($tWave, "WAVE", "WAVE") DllStructSetData($tWave, "FMT", "fmt ") DllStructSetData($tWave, "FMTLen", 16) DllStructSetData($tWave, "Format", $WAVE_FORMAT_PCM) ;Must be WAVE_FORMAT_PCM DllStructSetData($tWave, "Channels", _WFx_Get($pWFX, "nChannels")) DllStructSetData($tWave, "SampleRate", _WFx_Get($pWFX, "nSamplesPerSec")) DllStructSetData($tWave, "BytesPerSec", _WFx_Get($pWFX, "nAvgBytesPerSec")) DllStructSetData($tWave, "BlockAlign", _WFx_Get($pWFX, "nBlockAlign")) DllStructSetData($tWave, "BitsPerSample", _WFx_Get($pWFX, "wBitsPerSample")) DllStructSetData($tWave, "DATA", "data") EndIf Return $tWave EndFunc ;==>WavHeader_Init #ce ;See -> _WASAPI_RIFF_Header #EndRegion ### WAVE ### Func ListAvailableEndpoints() Local $aEP Local $aAvEPs = _WASAPI_EnumAudioEndpoints($eRender) ConsoleWrite("(" & $aAvEPs[0] & ") Available Render Endpoints:" & @CRLF) For $i = 1 To $aAvEPs[0] $aEP = _WASAPI_EndpointInfo($aAvEPs[$i]) ConsoleWrite($aEP[$eWASInfo_Name] & @CRLF) Next ConsoleWrite(@CRLF) $aAvEPs = _WASAPI_EnumAudioEndpoints($eCapture) ConsoleWrite("(" & $aAvEPs[0] & ") Available Capture Endpoints:" & @CRLF) For $i = 1 To $aAvEPs[0] $aEP = _WASAPI_EndpointInfo($aAvEPs[$i]) ConsoleWrite($aEP[$eWASInfo_Name] & @CRLF) Next ConsoleWrite(@CRLF & @CRLF) EndFunc ;==>ListAvailableEndpoints Func ListDeviceProperties($sDeviceID) Local $aInfo = _WASAPI_EndpointInfo($sDeviceID) ConsoleWrite("Device Name: " & $aInfo[$eWASInfo_Name] & @CRLF) ConsoleWrite("Description: " & $aInfo[$eWASinfo_Desc] & @CRLF) ConsoleWrite("Interface: " & $aInfo[$eWASinfo_Interface] & @CRLF) ConsoleWrite("System Name: " & $aInfo[$eWASinfo_SystemName] & @CRLF) ConsoleWrite("DeviceID: " & $aInfo[$eWASinfo_ID] & @CRLF) ConsoleWrite("GUID: " & $aInfo[$eWASinfo_GUID] & @CRLF) ConsoleWrite(@CRLF & @CRLF) EndFunc ;==>ListDeviceProperties Bilgus 1 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting. Link to comment Share on other sites More sharing options...
Bilgus Posted February 18, 2019 Author Share Posted February 18, 2019 I Fixed an error with Non Extensible wave formats that I introduced with the last change I also incorporated the changes argumentum posted above -- just be aware that not all devices pass back the silence flag and if the sound just recently stopped you probably won't receive the notification even on devices that do pass the flag.. argumentum 1 Link to comment Share on other sites More sharing options...
Bilgus Posted March 4, 2019 Author Share Posted March 4, 2019 Updated the UDF and example code No longer a WIP, I think it is pretty complete now I tried playback as well but unfortunately it will only play wav files in the same format as the soundcard READ same sample rate (bitrate, bits/sample) otherwise you need to do format conversion which is possible but I mainly was interested in this for the loopback interface and is thus beyond the scope I need it for Link to comment Share on other sites More sharing options...
Earthshine Posted March 5, 2019 Share Posted March 5, 2019 (edited) it does not even compile now. plus, could you please just attach the au3 files, this highlighting code isn't working for me, too much effort to test it. Edited March 5, 2019 by Earthshine My resources are limited. You must ask the right questions Link to comment Share on other sites More sharing options...
Bilgus Posted March 5, 2019 Author Share Posted March 5, 2019 @Earthshine I attached the files to the first post You know I attributed not being able to compile a script I copy and pasted to test last night to a user being an arse but perhaps its the forum software placing non-breaking white spaces into the code blocks... I suppose I owe him/her an apology. Link to comment Share on other sites More sharing options...
ptrex Posted July 21, 2019 Share Posted July 21, 2019 Might be interesting... Enum WASAPI OPBJECTShttps://matthewvaneerde.wordpress.com/2010/06/03/how-to-enumerate-wasapi-audio-processing-objects-apos-on-your-system/ https://github.com/mvaneerde/blog/tree/develop/apoenum Contributions :Firewall Log Analyzer for XP - Creating COM objects without a need of DLL's - UPnP support in AU3Crystal Reports Viewer - PDFCreator in AutoIT - Duplicate File FinderSQLite3 Database functionality - USB Monitoring - Reading Excel using SQLRun Au3 as a Windows Service - File Monitor - Embedded Flash PlayerDynamic Functions - Control Panel Applets - Digital Signing Code - Excel Grid In AutoIT - Constants for Special Folders in WindowsRead data from Any Windows Edit Control - SOAP and Web Services in AutoIT - Barcode Printing Using PS - AU3 on LightTD WebserverMS LogParser SQL Engine in AutoIT - ImageMagick Image Processing - Converter @ Dec - Hex - Bin -Email Address Encoder - MSI Editor - SNMP - MIB ProtocolFinancial Functions UDF - Set ACL Permissions - Syntax HighLighter for AU3ADOR.RecordSet approach - Real OCR - HTTP Disk - PDF Reader Personal Worldclock - MS Indexing Engine - Printing ControlsGuiListView - Navigation (break the 4000 Limit barrier) - Registration Free COM DLL Distribution - Update - WinRM SMART Analysis - COM Object Browser - Excel PivotTable Object - VLC Media Player - Windows LogOnOff Gui -Extract Data from Outlook to Word & Excel - Analyze Event ID 4226 - DotNet Compiler Wrapper - Powershell_COM - New Link to comment Share on other sites More sharing options...
faustf Posted January 30, 2020 Share Posted January 30, 2020 hi guys i try to use this UDF with Sample but i have 2 problem . 1 when i push ESC the program not stop it 2. i force the stop and when listen a file .raw i listen it bad 1 second listen good 2 second stay mute Bilgus 1 Link to comment Share on other sites More sharing options...
Bilgus Posted February 5, 2020 Author Share Posted February 5, 2020 (edited) Windows version? did you download or copy paste? any errors? the raw file is pcm at whatever sampling rate your sound card is set to what does this mean 'when listen a file .raw i listen it bad 1 second listen good 2 second stay mute'? also please post output of console.. Edited February 5, 2020 by Bilgus Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now