1

Тема: AHK: Запись mp3 с подрезкой тишины

Понадобилось для одного проекта записывать звук с подрезкой тишины с начала и с конца.
Написал гуи для ffmpeg.
https://www.videohelp.com/software/ffmpeg
ffmpeg.exe нужно будет переименовать в ffmpeg_script.exe.
Скрипт создает temp.wav, который потом стирает.
Результат сохраняется в %A_Now%.mp3.

NoiseGate := -70

#SingleInstance force
OnExit, MP3RecordGuiClose

LOAD_DLL_Mf_Mfplat()
MFStartup(version := 2, MFSTARTUP_FULL := 0)
MFCreateAttributes(pMFAttributes, 1)
IMFAttributes_SetGUID(pMFAttributes, MF_GUID(GUID, "MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE"), MF_GUID(GUID1, "MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID"))
MFEnumDeviceSources(pMFAttributes, pppSourceActivate, pcSourceActivate)
Loop % pcSourceActivate
{
   IMFActivate := NumGet(pppSourceActivate + (A_Index - 1)*A_PtrSize)
   name := IMFActivate_GetAllocatedString(IMFActivate, MF_GUID(GUID, "MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME"))
   if (A_Index = 1)
      RecordingDevices .= name "||"
   else
      RecordingDevices .= name "|"
   ObjRelease(IMFActivate)
}
DllCall("ole32\CoTaskMemFree", "ptr", pppSourceActivate)
ObjRelease(pMFAttributes)
MFShutdown()
UNLOAD_DLL_Mf_Mfplat()

Gui, MP3Record: +AlwaysOnTop -DpiScale
Gui, MP3Record: Add, Text, x12, RecordingDevice:
Gui, MP3Record: Add, DDL, vRecordingDevice x12 w250, % RecordingDevices
Gui, MP3Record: Add, Button, x12 y62 w120 h20 vRecord gMP3Record, Start Recording
Gui, MP3Record: Add, Text, x54 y86 w80 vMP3Time
Gui, MP3Record: Show
return

MP3Record:
GuiControlGet, RecordingDevice
if (CurrentState != "Record")
{
   loop
   {
      Process, Exist, ffmpeg_script.exe
      if ErrorLevel
      {
         Process, Close, ffmpeg_script.exe
         Process, WaitClose, ffmpeg_script.exe
      }
      else
         break
   }
   Run, ffmpeg_script.exe -f dshow -i audio="%RecordingDevice%" -acodec copy -y temp.wav, , hide, pid
   loop
   {
      if DllCall("AttachConsole", "uint", pid)
         break
      sleep 10
   }
   hConIn := DllCall("CreateFile", "str", "CONIN$", "uint", 0xC0000000, "uint", 0x3, "uint", 0, "uint", 0x3, "uint", 0, "uint", 0, "ptr")
   if (hConIn = -1)
   {
      msgbox error CreateFile %A_LastError%
      ExitApp
   }
   VarSetCapacity(ir, 24, 0)       ; ir := new INPUT_RECORD
   NumPut(1, ir, 0, "UShort")      ; ir.EventType := KEY_EVENT
   NumPut(1, ir, 8, "UShort")      ; ir.KeyEvent.wRepeatCount := 1
   min := sec := 0
   CurrentState := "Record"
   GuiControl,, Record, Stop Recording
   SetTimer, Count, 1000
}
else
{
   CurrentState := ""
   GuiControl,, Record, Start Recording
   SetTimer, Count, Off
   NumPut(Asc("q"), ir, 14, "UShort")
   NumPut(true, ir, 4, "Int")  ; ir.KeyEvent.bKeyDown := true
   if !DllCall("WriteConsoleInput", "ptr", hConIn, "ptr", &ir, "uint", 1, "uint*", 0)
   {
      msgbox error WriteConsoleInput one %A_LastError%
      ExitApp
   }
   NumPut(false, ir, 4, "Int") ; ir.KeyEvent.bKeyDown := false
   if !DllCall("WriteConsoleInput", "ptr", hConIn, "ptr", &ir, "uint", 1, "uint*", 0)
   {
      msgbox error WriteConsoleInput two %A_LastError%
      ExitApp
   }
   DllCall("CloseHandle", "ptr", hConIn)
   hConIn := ""
   DllCall("FreeConsole")
   Process, WaitClose, ffmpeg_script.exe
   MsgBox, 274340,, Save recorded mp3?
   IfMsgBox Yes
   {
      Run, ffmpeg_script.exe -i temp.wav -ab 320k -acodec libmp3lame -y -af "silenceremove=start_periods=1:start_duration=0:start_threshold=%NoiseGate%dB`,areverse`,silenceremove=start_periods=1:start_duration=0:start_threshold=%NoiseGate%dB`,areverse" "%A_Now%.mp3", , hide
      Process, WaitClose, ffmpeg_script.exe
   }
   GuiControl,, MP3Time
   FileDelete, temp.wav
}
return

Count:
sec++
if (sec = 60)
{
   min++
   sec := 0
}
GuiControl, MP3Record:, MP3Time, % "0:" Format("{1:02}", min) ":" Format("{1:02}", sec)
return

MP3RecordGuiClose:
loop
{
   Process, Exist, ffmpeg_script.exe
   if ErrorLevel
   {
      Process, Close, ffmpeg_script.exe
      Process, WaitClose, ffmpeg_script.exe
   }
   else
      break
}
FileDelete, temp.wav
ExitApp


LOAD_DLL_Mf_Mfplat()
{
   if !DllCall("GetModuleHandle","str","Mf")
      DllCall("LoadLibrary","Str", "Mf.dll", "ptr")
   if !DllCall("GetModuleHandle","str","Mfplat")
      DllCall("LoadLibrary","Str", "Mfplat.dll", "ptr")
}

UNLOAD_DLL_Mf_Mfplat()
{
   if hModule := DllCall("GetModuleHandle", "str", "Mf", "ptr")
      DllCall("FreeLibrary", "ptr", hModule)
   if hModule := DllCall("GetModuleHandle", "str", "Mfplat", "ptr")
      DllCall("FreeLibrary", "ptr", hModule)
}

MFStartup(version, dwFlags)
{
   hr := DllCall("Mfplat.dll\MFStartup", "uint", version, "uint", dwFlags)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

MFCreateAttributes(ByRef ppMFAttributes, cInitialSize)
{
   hr := DllCall("Mfplat.dll\MFCreateAttributes", "ptr*", ppMFAttributes, "uint", cInitialSize)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

IMFAttributes_SetGUID(this, guidKey, guidValue)
{
   hr := DllCall(NumGet(NumGet(this+0)+24*A_PtrSize), "ptr", this, "ptr", guidKey, "ptr", guidValue)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

MFEnumDeviceSources(pAttributes, ByRef pppSourceActivate, ByRef pcSourceActivate)
{
   hr := DllCall("Mf.dll\MFEnumDeviceSources", "ptr", pAttributes, "ptr*", pppSourceActivate, "uint*", pcSourceActivate)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

IMFActivate_GetAllocatedString(this, guidKey)
{
   hr := DllCall(NumGet(NumGet(this+0)+13*A_PtrSize), "ptr", this, "ptr", guidKey, "ptr*", ppwszValue, "uint*", pcchLength)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
   AllocatedString := StrGet(ppwszValue, pcchLength, "UTF-16")
   DllCall("ole32\CoTaskMemFree", "ptr", ppwszValue)
   return AllocatedString
}

MF_GUID(ByRef GUID, name)
{
   static init:=1, _:={}
   if init
   {
      init:=0
      _.MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE := [0xc60ac5fe, 0x252a, 0x478f, 0xa0, 0xef, 0xbc, 0x8f, 0xa5, 0xf7, 0xca, 0xd3]
      _.MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID := [0x14dd9a1c, 0x7cff, 0x41be, 0xb1, 0xb9, 0xba, 0x1a, 0xc6, 0xec, 0xb5, 0x71]
      _.MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME := [0x60d0e559, 0x52f8, 0x4fa2, 0xbb, 0xce, 0xac, 0xdb, 0x34, 0xa8, 0xec, 0x1]
   }
   if _.haskey(name)
   {
      p := _[name]
      VarSetCapacity(GUID,16)
      ,NumPut(p.1+(p.2<<32)+(p.3<<48),GUID,0,"int64")
      ,NumPut(p.4+(p.5<<8)+(p.6<<16)+(p.7<<24)+(p.8<<32)+(p.9<<40)+(p.10<<48)+(p.11<<56),GUID,8,"int64")
      return &GUID
   }
   else return name
}

_Error(val)
{
   msgbox % val
   ExitApp
}

MFShutdown()
{
   hr := DllCall("Mfplat.dll\MFShutdown")
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

Пример записи звука с внешнего источника с автоостопом при возникновении тишины меньшей чем -70дб, длящейся более 2 секунд.

SilenceDuration := 2000
NoiseGate := -70

#SingleInstance force
OnExit, MP3RecordGuiClose

LOAD_DLL_Mf_Mfplat()
MFStartup(version := 2, MFSTARTUP_FULL := 0)
MFCreateAttributes(pMFAttributes, 1)
IMFAttributes_SetGUID(pMFAttributes, MF_GUID(GUID, "MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE"), MF_GUID(GUID1, "MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID"))
MFEnumDeviceSources(pMFAttributes, pppSourceActivate, pcSourceActivate)
Loop % pcSourceActivate
{
   IMFActivate := NumGet(pppSourceActivate + (A_Index - 1)*A_PtrSize)
   name := IMFActivate_GetAllocatedString(IMFActivate, MF_GUID(GUID, "MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME"))
   if (A_Index = 1)
      RecordingDevices .= name "||"
   else
      RecordingDevices .= name "|"
   ObjRelease(IMFActivate)
}
DllCall("ole32\CoTaskMemFree", "ptr", pppSourceActivate)
ObjRelease(pMFAttributes)
MFShutdown()
UNLOAD_DLL_Mf_Mfplat()

Gui, MP3Record: +AlwaysOnTop -DpiScale
Gui, MP3Record: Add, Text, x12, RecordingDevice:
Gui, MP3Record: Add, DDL, vRecordingDevice x12 w250, % RecordingDevices
Gui, MP3Record: Add, Button, x12 y62 w120 h20 vRecord gMP3Record, Start Recording
Gui, MP3Record: Add, Text, x54 y86 w80 vMP3Time
Gui, MP3Record: Show
return

MP3Record:
GuiControlGet, RecordingDevice
loop
{
   Process, Exist, ffmpeg_script.exe
   if ErrorLevel
   {
      Process, Close, ffmpeg_script.exe
      Process, WaitClose, ffmpeg_script.exe
   }
   else
      break
}
Run, ffmpeg_script.exe -f dshow -i audio="%RecordingDevice%" -af silencedetect=noise=%NoiseGate%dB:d=0.1 -y temp.wav, , hide, pid
loop
{
   if DllCall("AttachConsole", "uint", pid)
      break
   sleep 10
}
min := sec := 0
GuiControl, Disable, Record
SetTimer, Count, 1000
hConOut := DllCall("CreateFile", "str", "CONOUT$", "uint", 0xC0000000, "uint", 0x3, "ptr", 0, "uint", 0x3, "uint", 0, "ptr", 0, "ptr")
if (hConOut = -1)
{
   msgbox error CreateFile CONOUT %A_LastError%
   ExitApp
}
loop
{
   VarSetCapacity(ConsoleScreenBufferInfo, 24, 0)
   if !DllCall("GetConsoleScreenBufferInfo", "ptr", hConOut, "ptr", &ConsoleScreenBufferInfo)
   {
      msgbox GetConsoleScreenBufferInfo failed - error %A_LastError%
      ExitApp
   }
   ConWinHeight := NumGet(ConsoleScreenBufferInfo, 6, "Short") + 1
   if (ConWinHeight = 0)
      ConWinHeight := NumGet(ConsoleScreenBufferInfo, 20, "Short") + 1
   if !ConWinWidth
      ConWinWidth := NumGet(ConsoleScreenBufferInfo, 18, "Short") + 1
   VarSetCapacity(ConsoleText, ConWinWidth*ConWinHeight*2, 0)
   if !DllCall("ReadConsoleOutputCharacter", "ptr", hConOut, "str", ConsoleText, "uint", ConWinWidth*ConWinHeight, "uint", 0, "ptr*", numCharsRead)
   {
      msgbox ReadConsoleOutputCharacter failed - error %A_LastError%.
      ExitApp
   }
   if !RecordingStart and InStr(ConsoleText, "time=00:")
      RecordingStart := 1
   if RecordingStart and !RecordingStartSound and (!InStr(ConsoleText, "silence_start:") or InStr(ConsoleText, "silence_end:"))
      RecordingStartSound := 1
   if RecordingStartSound
   {
      StrReplace(ConsoleText, "silence_start:", "silence_start:", silence_startCount)
      StrReplace(ConsoleText, "silence_end:", "silence_end:", silence_endCount)
      if (silence_startCount > silence_endCount)
      {
         if !SilenceTime
            SilenceTime := A_TickCount
         else if ((A_TickCount - SilenceTime) > SilenceDuration)
         {
            RecordingStart := RecordingStartSound := SilenceTime := ConWinWidth := ""
            DllCall("CloseHandle", "ptr", hConOut)
            break
         }
      }
      else 
         SilenceTime := ""
   }
   sleep 10
}
hConIn := DllCall("CreateFile", "str", "CONIN$", "uint", 0xC0000000, "uint", 0x3, "ptr", 0, "uint", 0x3, "uint", 0, "ptr", 0, "ptr")
if (hConIn = -1)
{
   msgbox error CreateFile CONIN %A_LastError%
   ExitApp
}
VarSetCapacity(ir, 24, 0)       ; ir := new INPUT_RECORD
NumPut(1, ir, 0, "UShort")      ; ir.EventType := KEY_EVENT
NumPut(1, ir, 8, "UShort")      ; ir.KeyEvent.wRepeatCount := 1
NumPut(Asc("q"), ir, 14, "UShort")
NumPut(true, ir, 4, "Int")  ; ir.KeyEvent.bKeyDown := true
if !DllCall("WriteConsoleInput", "ptr", hConIn, "ptr", &ir, "uint", 1, "uint*", 0)
{
   msgbox error WriteConsoleInput one %A_LastError%
   ExitApp
}
NumPut(false, ir, 4, "Int") ; ir.KeyEvent.bKeyDown := false
if !DllCall("WriteConsoleInput", "ptr", hConIn, "ptr", &ir, "uint", 1, "uint*", 0)
{
   msgbox error WriteConsoleInput two %A_LastError%
   ExitApp
}
DllCall("CloseHandle", "ptr", hConIn)
DllCall("FreeConsole")
SetTimer, Count, Off
GuiControl,, MP3Time
Process, WaitClose, ffmpeg_script.exe
Run, ffmpeg_script.exe -i temp.wav -ab 320k -acodec libmp3lame -y -af "silenceremove=start_periods=1:start_duration=0:start_threshold=%NoiseGate%dB`,areverse`,silenceremove=start_periods=1:start_duration=0:start_threshold=%NoiseGate%dB`,areverse" "%A_Now%.mp3", , hide
Process, WaitClose, ffmpeg_script.exe
FileDelete, temp.wav
msgbox done
GuiControl, Enable, Record
return


Count:
sec++
if (sec = 60)
{
   min++
   sec := 0
}
GuiControl, MP3Record:, MP3Time, % "0:" Format("{1:02}", min) ":" Format("{1:02}", sec)
return

MP3RecordGuiClose:
loop
{
   Process, Exist, ffmpeg_script.exe
   if ErrorLevel
   {
      Process, Close, ffmpeg_script.exe
      Process, WaitClose, ffmpeg_script.exe
   }
   else
      break
}
FileDelete, temp.wav
ExitApp


LOAD_DLL_Mf_Mfplat()
{
   if !DllCall("GetModuleHandle","str","Mf")
      DllCall("LoadLibrary","Str", "Mf.dll", "ptr")
   if !DllCall("GetModuleHandle","str","Mfplat")
      DllCall("LoadLibrary","Str", "Mfplat.dll", "ptr")
}

UNLOAD_DLL_Mf_Mfplat()
{
   if hModule := DllCall("GetModuleHandle", "str", "Mf", "ptr")
      DllCall("FreeLibrary", "ptr", hModule)
   if hModule := DllCall("GetModuleHandle", "str", "Mfplat", "ptr")
      DllCall("FreeLibrary", "ptr", hModule)
}

MFStartup(version, dwFlags)
{
   hr := DllCall("Mfplat.dll\MFStartup", "uint", version, "uint", dwFlags)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

MFCreateAttributes(ByRef ppMFAttributes, cInitialSize)
{
   hr := DllCall("Mfplat.dll\MFCreateAttributes", "ptr*", ppMFAttributes, "uint", cInitialSize)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

IMFAttributes_SetGUID(this, guidKey, guidValue)
{
   hr := DllCall(NumGet(NumGet(this+0)+24*A_PtrSize), "ptr", this, "ptr", guidKey, "ptr", guidValue)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

MFEnumDeviceSources(pAttributes, ByRef pppSourceActivate, ByRef pcSourceActivate)
{
   hr := DllCall("Mf.dll\MFEnumDeviceSources", "ptr", pAttributes, "ptr*", pppSourceActivate, "uint*", pcSourceActivate)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

IMFActivate_GetAllocatedString(this, guidKey)
{
   hr := DllCall(NumGet(NumGet(this+0)+13*A_PtrSize), "ptr", this, "ptr", guidKey, "ptr*", ppwszValue, "uint*", pcchLength)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
   AllocatedString := StrGet(ppwszValue, pcchLength, "UTF-16")
   DllCall("ole32\CoTaskMemFree", "ptr", ppwszValue)
   return AllocatedString
}

MF_GUID(ByRef GUID, name)
{
   static init:=1, _:={}
   if init
   {
      init:=0
      _.MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE := [0xc60ac5fe, 0x252a, 0x478f, 0xa0, 0xef, 0xbc, 0x8f, 0xa5, 0xf7, 0xca, 0xd3]
      _.MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID := [0x14dd9a1c, 0x7cff, 0x41be, 0xb1, 0xb9, 0xba, 0x1a, 0xc6, 0xec, 0xb5, 0x71]
      _.MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME := [0x60d0e559, 0x52f8, 0x4fa2, 0xbb, 0xce, 0xac, 0xdb, 0x34, 0xa8, 0xec, 0x1]
   }
   if _.haskey(name)
   {
      p := _[name]
      VarSetCapacity(GUID,16)
      ,NumPut(p.1+(p.2<<32)+(p.3<<48),GUID,0,"int64")
      ,NumPut(p.4+(p.5<<8)+(p.6<<16)+(p.7<<24)+(p.8<<32)+(p.9<<40)+(p.10<<48)+(p.11<<56),GUID,8,"int64")
      return &GUID
   }
   else return name
}

_Error(val)
{
   msgbox % val
   ExitApp
}

MFShutdown()
{
   hr := DllCall("Mfplat.dll\MFShutdown")
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

Тема для обсуждения