Author |
Topic: Get and set master sound level (Read 448 times) |
|
RobM
Junior Member
member is offline
Posts: 91
|
|
Get and set master sound level
« Thread started on: Oct 11th, 2016, 10:11pm » |
|
Does LBB have the ability to get and set the master sound level? Maybe through some BBC BASIC code?
|
|
Logged
|
|
|
|
Richard Russell
Administrator
member is offline
Posts: 1348
|
|
Re: Get and set master sound level
« Reply #1 on: Oct 11th, 2016, 10:52pm » |
|
on Oct 11th, 2016, 10:11pm, RobM wrote:Does LBB have the ability to get and set the master sound level? Maybe through some BBC BASIC code? |
|
On Windows Vista and later you can call ISimpleAudioVolume::SetMasterVolume. Although this is a call to a method in a COM object, so would once have been considered impossible in native LB code, I proved that not to be the case by creating my CallMethod function.
The difficulty is likely to be finding the IID of the ISimpleAudioVolume object and the layout of its vTable. It looks from MSDN as if they should be in the audioclient.h file in the Windows Platform SDK.
Richard.
|
|
|
|
RobM
Junior Member
member is offline
Posts: 91
|
|
Re: Get and set master sound level
« Reply #2 on: Oct 11th, 2016, 11:35pm » |
|
OK, thanks anyway. This project isn't important enough for me to learn COM object programming.
|
|
Logged
|
|
|
|
Rod
Full Member
member is offline
Gender:
Posts: 110
|
|
Re: Get and set master sound level
« Reply #3 on: Oct 12th, 2016, 06:55am » |
|
Why the master volume? The general idea now is that the user is in control of the master volume and that programs only get to alter their own volume level.
That allows co existence with other programs that may need to output sound.
If the user has sound off that may be for a good reason.
There are examples posted of setting the program volume, flashing a massage to confirm they can hear sound is possible, perhaps at the start of the program.
|
|
Logged
|
|
|
|
RobM
Junior Member
member is offline
Posts: 91
|
|
Re: Get and set master sound level
« Reply #4 on: Oct 12th, 2016, 2:55pm » |
|
In this case, I am the user.
I go on YouTube and often times need to turn up the volume to hear the video. Then I forget to turn it back down. A while later I'll get an email and my 'new mail' wav is too loud.
What I was going to do is have a program run in the background, checking the master sound level every 10 seconds. after it sees the level has gone up it will wait about 10 minutes and then turn it back down.
Like I said nothing real important
|
|
Logged
|
|
|
|
Richard Russell
Administrator
member is offline
Posts: 1348
|
|
Re: Get and set master sound level
« Reply #5 on: Oct 12th, 2016, 4:19pm » |
|
on Oct 12th, 2016, 2:55pm, RobM wrote:Like I said nothing real important |
|
Actually I think it's quite a neat idea - I might use it myself if you get it working. There's some complete C++ code here which doesn't look as if it ought to be too difficult to translate to Liberty BASIC using my CallMethod function.
If you don't want to do the translation I probably could quite quickly. If there were enough interest I could code it in a verbose step-by-step fashion to show how anything similar can be tackled.
Richard.
|
|
|
|
RobM
Junior Member
member is offline
Posts: 91
|
|
Re: Get and set master sound level
« Reply #6 on: Oct 12th, 2016, 4:40pm » |
|
If you would do it that would be great. I might be able to translate some of that c++ code but I don't really think I could figure out how to apply your CallMethod function where needed.
For the record, I was able to get my idea working along with an AutoHotKey program. It is really simple in that language, "Soundget, x" & "Soundset, x". But every time the program runs Windows changes the cursor to the app_starting (busy) cursor and is 100 times more annoying than having the volume set too high.
|
|
Logged
|
|
|
|
Richard Russell
Administrator
member is offline
Posts: 1348
|
|
Re: Get and set master sound level
« Reply #7 on: Oct 12th, 2016, 5:46pm » |
|
on Oct 12th, 2016, 4:19pm, Richard Russell wrote:If there were enough interest I could code it in a verbose step-by-step fashion |
|
What say the assembled members? Is it worth the extra effort of me doing that? Is anybody (except me) ever likely to try writing COM-object code in Liberty BASIC?
Richard.
|
|
Logged
|
|
|
|
RobM
Junior Member
member is offline
Posts: 91
|
|
Re: Get and set master sound level
« Reply #8 on: Oct 12th, 2016, 5:56pm » |
|
Speaking for myself I would have to say I don't know if I would use it for anything other than this current idea. Knowing in the back of my mind that COM objects weren't possible with LB I never looked into what was possible in using them.
Offhand, do you have a general list of things that could be accomplished.
|
|
Logged
|
|
|
|
Richard Russell
Administrator
member is offline
Posts: 1348
|
|
Re: Get and set master sound level
« Reply #9 on: Oct 13th, 2016, 7:29pm » |
|
on Oct 12th, 2016, 5:56pm, RobM wrote:Speaking for myself I would have to say I don't know if I would use it for anything other than this current idea. |
|
OK, fair enough. There's a huge range of COM-based Windows API functions - pretty much any API introduced in the last ten years or so is probably a COM interface - but if it's not something you've ever wanted up to now, you probably never will.
Here's a direct, but not heavily commented, translation of the C++ code; it works in both LB 4 and LBB. It should be compatible with Windows Vista onwards:
Code: ' Translated to Liberty BASIC by Richard Russell from the code here, 13-Oct-2016:
' http://www.codeproject.com/Tips/233484/Change-Master-Volume-in-Visual-Cplusplus
result = ChangeVolume(0.5, 1)
end
function ChangeVolume(nVolume, bScalar)
open "ole32.dll" for dll as #ole32
open "oleaut32.dll" for dll as #oleaut32
calldll #ole32, "CoInitialize", 0 as long, r as void
IID.MMDeviceEnumerator$ = UUID$("{A95664D2-9614-4F35-A746-DE8DB63617E6}")
CLSID.MMDeviceEnumerator$ = UUID$("{BCDE0395-E52F-467C-8E3D-C4579291692E}")
IID.IAudioEndpointVolume$ = UUID$("{5CDF2C82-841E-4546-9722-0CF74078229A}")
CLSCTX.INPROC.SERVER = 1
eRender = 0
eConsole = 0
Release = 2
Activate = 3
GetDefaultAudioEndpoint = 4
SetMasterVolumeLevel = 6
SetMasterVolumeLevelScalar = 7
GetMasterVolumeLevel = 8
GetMasterVolumeLevelScalar = 9
struct IMM, deviceEnumerator as ulong
calldll #ole32, "CoCreateInstance", CLSID.MMDeviceEnumerator$ as ptr, _
0 as long, CLSCTX.INPROC.SERVER as long, IID.MMDeviceEnumerator$ as ptr, _
IMM as struct, hr as long
' hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
struct default, Device as ulong
struct parms, dataFlow as long, role as long, ppDevice as ptr
parms.dataFlow.struct = eRender
parms.role.struct = eConsole
parms.ppDevice.struct = default.struct
hr = CallMethod(IMM.deviceEnumerator.struct, GetDefaultAudioEndpoint, parms.struct)
default.struct = parms.ppDevice.struct
' hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume),
' CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume);
struct Iaudio, endpointVolume as ulong
struct parms, iid as ptr, dwClsCtx as long, _
pActivationParams as ulong, ppInterface as ptr
parms.iid.struct = IID.IAudioEndpointVolume$
parms.dwClsCtx.struct = CLSCTX.INPROC.SERVER
parms.ppInterface.struct = Iaudio.struct
hr = CallMethod(default.Device.struct, Activate, parms.struct)
Iaudio.struct = parms.ppInterface.struct
' defaultDevice->Release();
hr = CallMethod(default.Device.struct, Release, "")
default.Device.struct = 0
' endpointVolume->GetMasterVolumeLevel(¤tVolume);
struct Volume, float as long
struct parms, pfLevel as ptr
parms.pfLevel.struct = Volume.struct
hr = CallMethod(Iaudio.endpointVolume.struct, GetMasterVolumeLevel, parms.struct)
Volume.struct = parms.pfLevel.struct
' Convert float to double:
struct current, Volume as double
float = Volume.float.struct
calldll #oleaut32, "VarR8FromR4", float as long, current as struct, r as void
print "Current volume (dB) = "; current.Volume.struct
' hr = endpointVolume->GetMasterVolumeLevelScalar(¤tVolume);
hr = CallMethod(Iaudio.endpointVolume.struct, GetMasterVolumeLevelScalar, parms.struct)
Volume.struct = parms.pfLevel.struct
' Convert float to double:
float = Volume.float.struct
calldll #oleaut32, "VarR8FromR4", float as long, current as struct, r as void
print "Current volume (scalar) = "; current.Volume.struct
' Set new volume:
struct parms, fLevel as ptr, EventContext as long
calldll #oleaut32, "VarR4FromR8", nVolume as double, parms as struct, r as void
if bScalar = 0 then
hr = CallMethod(Iaudio.endpointVolume.struct, SetMasterVolumeLevel, parms.struct)
else
hr = CallMethod(Iaudio.endpointVolume.struct, SetMasterVolumeLevelScalar, parms.struct)
end if
' endpointVolume->Release();
hr = CallMethod(Iaudio.endpointVolume.struct, Release, "")
Iaudio.endpointVolume.struct = 0
calldll #ole32, "CoUninitialize", r as void
close #oleaut32
close #ole32
end function
function UUID$(iid$)
l = len(iid$) + 1
wide$ = space$(2 * l) + chr$(0)
calldll #kernel32, "MultiByteToWideChar", 0 as long, 0 as long, _
iid$ as ptr, -1 as long, wide$ as ptr, l as long, r as long
UUID$ = space$(16) + chr$(0)
calldll #ole32, "CLSIDFromString", wide$ as ptr, UUID$ as ptr, r as long
end function
function CallMethod(object, method, parm$)
code$ = chr$(139)+"D$"+chr$(4)+chr$(139)+"T$"+chr$(8)+chr$(139)+"L$" _
+ chr$(16)+"VW"+chr$(139)+"t$"+chr$(20)+chr$(43)+chr$(225)+chr$(139) _
+ chr$(252)+chr$(243)+chr$(164)+chr$(80)+chr$(139)+chr$(0)+chr$(255) _
+ chr$(20)+chr$(144)+chr$(95)+chr$(94)+chr$(194)+chr$(16)+chr$(0)
p$ = parm$
n = len(p$)
calldll #user32, "CallWindowProcA", code$ as ptr, object as long,_
method as long, p$ as ptr, n as long, CallMethod as long
end function It's really annoying that structs are global in LB, since it means I can't make this a library function that can safely be called from any program.
Richard.
|
|
|
|
RobM
Junior Member
member is offline
Posts: 91
|
|
Re: Get and set master sound level
« Reply #10 on: Oct 13th, 2016, 10:59pm » |
|
Thanks Richard!
I think I will add one more parameter to the function to specify whether it should check the sound level or change it. Looks like that should be easy enough.
|
|
Logged
|
|
|
|
RobM
Junior Member
member is offline
Posts: 91
|
|
Re: Get and set master sound level
« Reply #11 on: Oct 14th, 2016, 03:15am » |
|
I ended up splitting it into a function & a sub and removed some (but not all) unneeded code. As is, this code will lower your volume 6 seconds after it sees it over 10.
My loops here are pretty ugly coding but this is just a quick demo :)
Thanks again Richard.
Code: DesiredVolume=10'volume as a percentage from 0 to 100, as Windows shows it in the master volume control
ResetTime=.1'time in minutes to wait after volume has been increased
[loop1]
scan
calldll #kernel32, "Sleep", 10 as long, re as void
CurrentVolume=GetVolume()
if CurrentVolume > DesiredVolume then
StartTime=GetTickCount()
goto [loop2]
end if
goto [loop1]
[loop2]
scan
calldll #kernel32, "Sleep", 10 as long, re as void
CurrentTime=GetTickCount()
if CurrentTime-StartTime > ResetTime*60*1000 then
call SetVolume DesiredVolume,1
goto [loop1]
end if
goto [loop2]
function GetVolume()
open "ole32.dll" for dll as #ole32
open "oleaut32.dll" for dll as #oleaut32
calldll #ole32, "CoInitialize", 0 as long, r as void
IID.MMDeviceEnumerator$ = UUID$("{A95664D2-9614-4F35-A746-DE8DB63617E6}")
CLSID.MMDeviceEnumerator$ = UUID$("{BCDE0395-E52F-467C-8E3D-C4579291692E}")
IID.IAudioEndpointVolume$ = UUID$("{5CDF2C82-841E-4546-9722-0CF74078229A}")
CLSCTX.INPROC.SERVER = 1
eRender = 0
eConsole = 0
Release = 2
Activate = 3
GetDefaultAudioEndpoint = 4
GetMasterVolumeLevel = 8
GetMasterVolumeLevelScalar = 9
struct IMM, deviceEnumerator as ulong
calldll #ole32, "CoCreateInstance", CLSID.MMDeviceEnumerator$ as ptr, _
0 as long, CLSCTX.INPROC.SERVER as long, IID.MMDeviceEnumerator$ as ptr, _
IMM as struct, hr as long
' hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
struct default, Device as ulong
struct parms, dataFlow as long, role as long, ppDevice as ptr
parms.dataFlow.struct = eRender
parms.role.struct = eConsole
parms.ppDevice.struct = default.struct
hr = CallMethod(IMM.deviceEnumerator.struct, GetDefaultAudioEndpoint, parms.struct)
default.struct = parms.ppDevice.struct
' hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume),
' CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume);
struct Iaudio, endpointVolume as ulong
struct parms, iid as ptr, dwClsCtx as long, _
pActivationParams as ulong, ppInterface as ptr
parms.iid.struct = IID.IAudioEndpointVolume$
parms.dwClsCtx.struct = CLSCTX.INPROC.SERVER
parms.ppInterface.struct = Iaudio.struct
hr = CallMethod(default.Device.struct, Activate, parms.struct)
Iaudio.struct = parms.ppInterface.struct
' defaultDevice->Release();
hr = CallMethod(default.Device.struct, Release, "")
default.Device.struct = 0
' endpointVolume->GetMasterVolumeLevel(¤tVolume);
struct Volume, float as long
struct parms, pfLevel as ptr
parms.pfLevel.struct = Volume.struct
hr = CallMethod(Iaudio.endpointVolume.struct, GetMasterVolumeLevel, parms.struct)
Volume.struct = parms.pfLevel.struct
' Convert float to double:
struct current, Volume as double
float = Volume.float.struct
calldll #oleaut32, "VarR8FromR4", float as long, current as struct, r as void
' hr = endpointVolume->GetMasterVolumeLevelScalar(¤tVolume);
hr = CallMethod(Iaudio.endpointVolume.struct, GetMasterVolumeLevelScalar, parms.struct)
Volume.struct = parms.pfLevel.struct
' Convert float to double:
float = Volume.float.struct
calldll #oleaut32, "VarR8FromR4", float as long, current as struct, r as void
GetVolume=int(current.Volume.struct*100+1)
' endpointVolume->Release();
hr = CallMethod(Iaudio.endpointVolume.struct, Release, "")
Iaudio.endpointVolume.struct = 0
calldll #ole32, "CoUninitialize", r as void
close #oleaut32
close #ole32
end function
sub SetVolume nVolume, bScalar
nVolume=nVolume/100
open "ole32.dll" for dll as #ole32
open "oleaut32.dll" for dll as #oleaut32
calldll #ole32, "CoInitialize", 0 as long, r as void
IID.MMDeviceEnumerator$ = UUID$("{A95664D2-9614-4F35-A746-DE8DB63617E6}")
CLSID.MMDeviceEnumerator$ = UUID$("{BCDE0395-E52F-467C-8E3D-C4579291692E}")
IID.IAudioEndpointVolume$ = UUID$("{5CDF2C82-841E-4546-9722-0CF74078229A}")
CLSCTX.INPROC.SERVER = 1
eRender = 0
eConsole = 0
Release = 2
Activate = 3
GetDefaultAudioEndpoint = 4
SetMasterVolumeLevelScalar = 7
struct IMM, deviceEnumerator as ulong
calldll #ole32, "CoCreateInstance", CLSID.MMDeviceEnumerator$ as ptr, _
0 as long, CLSCTX.INPROC.SERVER as long, IID.MMDeviceEnumerator$ as ptr, _
IMM as struct, hr as long
' hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
struct default, Device as ulong
struct parms, dataFlow as long, role as long, ppDevice as ptr
parms.dataFlow.struct = eRender
parms.role.struct = eConsole
parms.ppDevice.struct = default.struct
hr = CallMethod(IMM.deviceEnumerator.struct, GetDefaultAudioEndpoint, parms.struct)
default.struct = parms.ppDevice.struct
' hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume),
' CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume);
struct Iaudio, endpointVolume as ulong
struct parms, iid as ptr, dwClsCtx as long, _
pActivationParams as ulong, ppInterface as ptr
parms.iid.struct = IID.IAudioEndpointVolume$
parms.dwClsCtx.struct = CLSCTX.INPROC.SERVER
parms.ppInterface.struct = Iaudio.struct
hr = CallMethod(default.Device.struct, Activate, parms.struct)
Iaudio.struct = parms.ppInterface.struct
' defaultDevice->Release();
hr = CallMethod(default.Device.struct, Release, "")
default.Device.struct = 0
' Set new volume:
struct Volume, float as long
struct parms, fLevel as ptr, EventContext as long
calldll #oleaut32, "VarR4FromR8", nVolume as double, parms as struct, r as void
hr = CallMethod(Iaudio.endpointVolume.struct, SetMasterVolumeLevelScalar, parms.struct)
' endpointVolume->Release();
hr = CallMethod(Iaudio.endpointVolume.struct, Release, "")
Iaudio.endpointVolume.struct = 0
calldll #ole32, "CoUninitialize", r as void
close #oleaut32
close #ole32
end sub
function UUID$(iid$)
l = len(iid$) + 1
wide$ = space$(2 * l) + chr$(0)
calldll #kernel32, "MultiByteToWideChar", 0 as long, 0 as long, _
iid$ as ptr, -1 as long, wide$ as ptr, l as long, r as long
UUID$ = space$(16) + chr$(0)
calldll #ole32, "CLSIDFromString", wide$ as ptr, UUID$ as ptr, r as long
end function
function CallMethod(object, method, parm$)
code$ = chr$(139)+"D$"+chr$(4)+chr$(139)+"T$"+chr$(8)+chr$(139)+"L$" _
+ chr$(16)+"VW"+chr$(139)+"t$"+chr$(20)+chr$(43)+chr$(225)+chr$(139) _
+ chr$(252)+chr$(243)+chr$(164)+chr$(80)+chr$(139)+chr$(0)+chr$(255) _
+ chr$(20)+chr$(144)+chr$(95)+chr$(94)+chr$(194)+chr$(16)+chr$(0)
p$ = parm$
n = len(p$)
calldll #user32, "CallWindowProcA", code$ as ptr, object as long,_
method as long, p$ as ptr, n as long, CallMethod as long
end function
function GetTickCount()
calldll #kernel32, "GetTickCount",_
GetTickCount as ulong
end function
|
« Last Edit: Oct 14th, 2016, 03:16am by RobM » |
Logged
|
|
|
|
RobM
Junior Member
member is offline
Posts: 91
|
|
Re: Get and set master sound level
« Reply #12 on: Oct 14th, 2016, 06:12am » |
|
The two loops in the code above can be replaced with this this. Makes it easier to insert into a program with a single loop ;)
Code:[loop]
scan
calldll #kernel32, "Sleep", 10 as long, re as void
CurrentTime=GetTickCount()
CurrentVolume=GetVolume()
if CurrentVolume>DesiredVolume and CountDownStarted=0 then
StartTime=GetTickCount()
CountDownStarted=1
end if
if CurrentTime-StartTime > ResetTime*60*1000 then
call SetVolume DesiredVolume, 1
CountDownStarted=0
end if
goto [loop]
|
« Last Edit: Oct 14th, 2016, 06:13am by RobM » |
Logged
|
|
|
|
|