michielp1807
michielp1807

Reputation: 33

Output Sound To Audio Device

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?

Upvotes: 0

Views: 1466

Answers (3)

ibrahim mert
ibrahim mert

Reputation: 41

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
}

Upvotes: 0

michielp1807
michielp1807

Reputation: 33

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.

Upvotes: 2

PGilm
PGilm

Reputation: 2312

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,

Upvotes: 0

Related Questions