0

I'm trying to create a script that will trigger a sound effect when a hotkey is pressed, this might not seem too difficult, but I also want to be able to output it to a specific audio device, a virtual audio cable in my case.

I've looked around on the internet a bit and I saw many possible solutions, but none have worked out for me (yet). It appears that the normal SoundPlay function in AHK can not output to a specific audio device, but I'm also looking into other options, like playing the sound via a batch script with wmplayer or other media players, but I can't find a solutions that will let me output the sound to a specific audio devices...

So my question is, what's the best way to play sound to a specific non-default audio device (a virtual audio cable) which can be done inside a command prompt or inside autohotkey?

double-beep
  • 5,031
  • 17
  • 33
  • 41

3 Answers3

2

So I managed to do what I was trying to accomplish. I'll tell you how I did it:

After some searching around on the internet I came across a C# library called IrrKlang. Using IrrKlang I made a little console app program which I can call by playsound soundfile.mp3 0, playsound is the the name of the .exe, the first parameter is the path to the soundfile from the location of the playsound.exe, and the last parameter is a number which is used to choose the audio device, which number this should be is still guess work but after some trial and error you can find the number of your virtual audio cable or other audio device.

For the people who come here in the future I've put my code up on github.

  • 1
    While the content of your project, or a minimal portion of it, might make for a good answer, this answer, as currently written, doesn't really answer the question. Answers (and questions) are expected to be self-contained. They should not be "go find the answer at [link]", or "please contact me for code". The primary purpose of Stack Overflow (actually, all of Stack Exchange) is to build a repository of questions and answers which are helpful to future visitors. Requiring people to go elsewhere, or contact someone in order to get the actual answer really isn't compatible with that. – Makyen Sep 26 '20 at 00:37
0

Everything you need is here:

Lexikos's Vista Audio Control Functions

Note: SoundSet and SoundGet on AutoHotkey v1.1.10 and later support Vista and later natively. You don't need VA.ahk unless you want to use advanced functions not supported by SoundSet/SoundGet.

https://autohotkey.com/board/topic/21984-vista-audio-control-functions/

To change the default output device, you can script the Sound properties in cpanel:

Run, mmsys.cpl
WinWait, Sound
    ControlSend, SysListView321,{Down num}  ' num matches device position
    Sleep, 100
    ControlClick, &Set Default
    Sleep, 100
    ControlClick, OK
WinWaitClose, Sound

Hth,

PGilm
  • 2,262
  • 1
  • 14
  • 26
  • Thanks for your reply! How would I go about playing a sound to a specific audio device using Vista Audio Control? I see in the documentation that there's a function VA_GetDevice to get a certain device, but how do I play a sound on that device? – MichielP1807 Apr 07 '17 at 14:04
  • VA gets you which device is in use. After that, I have scripted the cpanel to select a different one. But this doesn't help in directing sound to an arbitrary device regardless of cpanel default sound device settings (see amended answer). Some applications let you do this, though. – PGilm Apr 07 '17 at 16:18
0

I modified one code i found, and add volume up and down. You have to chance device1 and device2 names

  • Alt + Wheel Up = Volume Up
  • Alt + Wheel Down = Volume Down
  • Ctrl + F12 = Chance Device

glhf!

device1:="Speakers / Headphones"
device2:="Communications Headphones"
; http://www.daveamenta.com/2011-05/programmatically-or-command-line-change-the-default-sound-playback-device-in-windows-7/
Devices := {}
IMMDeviceEnumerator := ComObjCreate("{BCDE0395-E52F-467C-8E3D-C4579291692E}", "{A95664D2-9614-4F35-A746-DE8DB63617E6}")

; IMMDeviceEnumerator::EnumAudioEndpoints
; eRender = 0, eCapture, eAll
; 0x1 = DEVICE_STATE_ACTIVE
DllCall(NumGet(NumGet(IMMDeviceEnumerator+0)+3*A_PtrSize), "UPtr", IMMDeviceEnumerator, "UInt", 0, "UInt", 0x1, "UPtrP", IMMDeviceCollection, "UInt")
ObjRelease(IMMDeviceEnumerator)

; IMMDeviceCollection::GetCount
DllCall(NumGet(NumGet(IMMDeviceCollection+0)+3*A_PtrSize), "UPtr", IMMDeviceCollection, "UIntP", Count, "UInt")
Loop % (Count)
{
    ; IMMDeviceCollection::Item
    DllCall(NumGet(NumGet(IMMDeviceCollection+0)+4*A_PtrSize), "UPtr", IMMDeviceCollection, "UInt", A_Index-1, "UPtrP", IMMDevice, "UInt")

    ; IMMDevice::GetId
    DllCall(NumGet(NumGet(IMMDevice+0)+5*A_PtrSize), "UPtr", IMMDevice, "UPtrP", pBuffer, "UInt")
    DeviceID := StrGet(pBuffer, "UTF-16"), DllCall("Ole32.dll\CoTaskMemFree", "UPtr", pBuffer)

    ; IMMDevice::OpenPropertyStore
    ; 0x0 = STGM_READ
    DllCall(NumGet(NumGet(IMMDevice+0)+4*A_PtrSize), "UPtr", IMMDevice, "UInt", 0x0, "UPtrP", IPropertyStore, "UInt")
    ObjRelease(IMMDevice)

    ; IPropertyStore::GetValue
    VarSetCapacity(PROPVARIANT, A_PtrSize == 4 ? 16 : 24)
    VarSetCapacity(PROPERTYKEY, 20)
    DllCall("Ole32.dll\CLSIDFromString", "Str", "{A45C254E-DF1C-4EFD-8020-67D146A850E0}", "UPtr", &PROPERTYKEY)
    NumPut(14, &PROPERTYKEY + 16, "UInt")
    DllCall(NumGet(NumGet(IPropertyStore+0)+5*A_PtrSize), "UPtr", IPropertyStore, "UPtr", &PROPERTYKEY, "UPtr", &PROPVARIANT, "UInt")
    DeviceName := StrGet(NumGet(&PROPVARIANT + 8), "UTF-16")    ; LPWSTR PROPVARIANT.pwszVal
    DllCall("Ole32.dll\CoTaskMemFree", "UPtr", NumGet(&PROPVARIANT + 8))    ; LPWSTR PROPVARIANT.pwszVal
    ObjRelease(IPropertyStore)

    ObjRawSet(Devices, DeviceName, DeviceID)
}
ObjRelease(IMMDeviceCollection)
Return

$!WheelUp::Send {Volume_Up 5}
$!WheelDown::Send {Volume_Down 5}
currentDevice:=false
^F12:: 
    currentDevice:=!currentDevice
    if currentDevice
        SetDefaultEndpoint( GetDeviceID(Devices, device1) )
    else
        SetDefaultEndpoint( GetDeviceID(Devices, device2) )
return

SetDefaultEndpoint(DeviceID)
{
    IPolicyConfig := ComObjCreate("{870af99c-171d-4f9e-af0d-e63df40c2bc9}", "{F8679F50-850A-41CF-9C72-430F290290C8}")
    DllCall(NumGet(NumGet(IPolicyConfig+0)+13*A_PtrSize), "UPtr", IPolicyConfig, "UPtr", &DeviceID, "UInt", 0, "UInt")
    ObjRelease(IPolicyConfig)
}

GetDeviceID(Devices, Name)
{
    For DeviceName, DeviceID in Devices
        If (InStr(DeviceName, Name))
            Return DeviceID
}