Pass frequency and duration pairs to SoundBeep() or SoundBeepAsync()
It creates a wav file matching the beep pairs' pattern, saves this in %TEMP% (so as not to unnecessarily duplicate the process of creating it with other scripts or across reloads), then loads that into memory and caches it for use with PlaySound
If given a sparse array it fills in the gaps with SoundBeep's defaults (523/150)
script
Code: Select all
SoundBeep(beeps*) {
return SoundBeepNew(beeps)
}
SoundBeepAsync(beeps*) {
return SoundBeepNew(beeps, 1)
}
SoundBeepNew(beeps, async=0, frequency=523, duration=150) {
static cached := []
beep_arr := []
Loop, % if2(!beeps.Count(), beeps.maxIndex())
if (Mod(A_Index, 2))
beep_arr.Push([if2(beeps[A_Index],frequency), if2(beeps[A_Index+1],duration)])
if (cached.HasKey(file := BeepFile(beep_arr)))
sound := cached[file]
else
sound := cached[file] := Sound(BeepToFile(beep_arr, file))
return PlaySound(sound, async)
}
BeepFile(beeps) {
return arrJoin([A_Temp, A_ThisFunc, arrJoin(beeps, ",") ".wav"], "\")
}
BeepToFile(beeps, file) { ;; https://www.autohotkey.com/boards/viewtopic.php?t=34584
static vSamplesPerSec := 44100
static vBitsPerSample := 8
static vChannels := 1
static vBytesPerSample := vBitsPerSample/8
if (!FileExist(file))
{
vDuration := 0
for each, beep in beeps
vDuration += beep.2
vDuration := vDuration/1000
vSamples := vSamplesPerSec*vDuration
vSizeData := vChannels * vSamples * vBytesPerSample
VarSetCapacity(vData, 58+vSizeData, 0)
StrPut("RIFF", &vData+0, "CP0")
NumPut(50 + vSizeData, vData, 4)
StrPut("WAVE", &vData+8, "CP0")
StrPut("fmt ", &vData+12, "CP0")
NumPut(18, vData, 16)
NumPut(1, vData, 20, "UShort")
NumPut(vChannels, vData, 22, "UShort")
NumPut(vSamplesPerSec, vData, 24)
NumPut(vChannels*vSamplesPerSec*vBytesPerSample, vData, 28)
NumPut(vChannels*vBytesPerSample, vData, 32, "UShort")
NumPut(vBitsPerSample, vData, 34, "UShort")
NumPut(0, vData, 36, "UShort")
StrPut("fact", &vData+38, "CP0")
NumPut(4, vData, 42)
NumPut(vSamples, vData, 46)
StrPut("data", &vData+50, "CP0")
NumPut(vSamples*vChannels*vBytesPerSample, vData, 54)
vAddr := &vData + 58
vEnd := &vData + 58 + vSizeData
for each, beep in beeps
{
vFreq := beep.1
vDuration := beep.2
vSamplesPerDuration := Floor((vSamplesPerSec*vDuration)/1000)
vSamplesPerWave := Floor(vSamplesPerSec/vFreq)
if (vSamplesPerWave <= 0)
break
vEndWave := vAddr + vSamplesPerDuration*vBytesPerSample
Loop
{
if (vAddr + vSamplesPerWave*vBytesPerSample > vEnd)
|| (vAddr + vSamplesPerWave*vBytesPerSample > vEndWave)
break
JEE_WaveWriteNote(vAddr, vSamplesPerWave, vBytesPerSample)
vAddr += vSamplesPerWave*vBytesPerSample
}
}
FileCreateDir, % Dir(file)
oFile := FileOpen(file, "w", "CP1252")
oFile.RawWrite(vData, 58+vSizeData)
oFile.Close()
}
return file
}
JEE_WaveWriteNote(vAddr, vSamplesPerWave, vBytesPerSample) {
if (vBytesPerSample = 1)
vPtrType := "UChar", vNum1 := 1, vNum2 := 127
else if (vBytesPerSample = 2)
vPtrType := "Short", vNum1 := 0, vNum2 := 32767
else
return
vAdjust := 360 / vSamplesPerWave
vTemp := 0
vList := ""
Loop, % Floor(vSamplesPerWave)
{
vNum := Round((Sin(vTemp*0.01745329252)+vNum1)*vNum2)
vList .= vNum ","
vTemp += vAdjust
NumPut(vNum, vAddr+0, (A_Index-1)*vBytesPerSample, vPtrType)
}
}
Sound(file) {
static sounds := []
static sounds_len := 0
global
if (sounds.HasKey(file))
return sounds[file]
else
{
local sound_var := arrJoin([A_ThisFunc, sounds_len += 1, A_TickCount], "_")
local oFile := FileOpen(file, "r", "CP1252")
oFile.RawRead(%sound_var%, FileGetSize(file))
oFile.Close()
return sounds[file] := &%sound_var%
}
}
PlaySound(sound, async=0) { ;; https://www.autohotkey.com/board/topic/57631-crazy-scripting-resource-only-dll-for-dummies-36l-v07/page-4#entry609282
static SND_SYNC := 0x00000000 ;; play synchronously (default)
static SND_ASYNC := 0x00000001 ;; play asynchronously
static SND_NODEFAULT := 0x00000002 ;; silence (!default) if sound not found
static SND_MEMORY := 0x00000004 ;; pszSound points to a memory file
static SND_LOOP := 0x00000008 ;; loop the sound until next sndPlaySound
static SND_NOSTOP := 0x00000010 ;; don't stop any currently playing sound
static SND_NOWAIT := 0x00002000 ;; don't wait if the driver is busy
static SND_ALIAS := 0x00010000 ;; name is a registry alias
static SND_ALIAS_ID := 0x00110000 ;; alias is a predefined ID
static SND_FILENAME := 0x00020000 ;; name is file name
static SND_RESOURCE := 0x00040004 ;; name is resource name or atom
static SND_PURGE := 0x00000040 ;; purge non-static events for task
static SND_APPLICATION := 0x00000080 ;; look for application specific association
static SND_SENTRY := 0x00080000 ;; Generate a SoundSentry event with this sound
static SND_RING := 0x00100000 ;; Treat this as a "ring" from a communications app - don't duck me
static SND_SYSTEM := 0x00200000 ;; Treat this as a system sound
return DllCall("winmm.dll\PlaySound" (A_IsUnicode ? "W" : "A"), "UInt",sound, "UInt",0 , "UInt",SND_NODEFAULT | SND_MEMORY | (async ? SND_ASYNC : SND_SYNC))
}
if2(a, b="") {
return a ? a : b
}
arrJoin(arr, del="") {
out := ""
for k, v in arr
out .= del (isObject(v) ? arrJoin(v, del) : v)
return SubStr(out, 1+StrLen(del))
}
Dir(Path, r=1) {
Loop, %r%
SplitPath, Path,, Path
return Path
}
FileGetSize(Filename="", Units="") {
FileGetSize, v, %Filename%, %Units%
Return, v
}
Code: Select all
Esc::ExitApp
1::SoundBeep(1000,150, 500,150, 250,150)
;; ==
2::SoundBeep(1000, , 500, , 250)
3::SoundBeep(523)
;; ==
4::SoundBeep()
;; doesn't stop execution, but can be interrupted by another beep generated by its own script
q::SoundBeepAsync(250,250, 500,250, 1000,250)
w::SoundBeepAsync(2500,300, 5000)
e::SoundBeepAsync(,, 5000,, 250,, 400)