LB Booster
« Get and set master sound level »

Welcome Guest. Please Login or Register.
Apr 1st, 2018, 03:56am



ATTENTION MEMBERS: Conforums will be closing it doors and discontinuing its service on April 15, 2018.
We apologize Conforums does not have any export functions to migrate data.
Ad-Free has been deactivated. Outstanding Ad-Free credits will be reimbursed to respective payment methods.

Thank you Conforums members.
Speed up Liberty BASIC programs by up to ten times!
Compile Liberty BASIC programs to compact, standalone executables!
Overcome many of Liberty BASIC's bugs and limitations!
LB Booster Resources
LB Booster documentation
LB Booster Home Page
LB Booster technical Wiki
Just BASIC forum
BBC BASIC Home Page
Liberty BASIC forum (the original)

« Previous Topic | Next Topic »
Pages: 1  Notify Send Topic Print
 thread  Author  Topic: Get and set master sound level  (Read 449 times)
RobM
Junior Member
ImageImage


member is offline

Avatar




PM


Posts: 91
xx 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?
User IP Logged

Richard Russell
Administrator
ImageImageImageImageImage


member is offline

Avatar




Homepage PM


Posts: 1348
xx 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.
« Last Edit: Oct 11th, 2016, 10:55pm by Richard Russell » User IP Logged

RobM
Junior Member
ImageImage


member is offline

Avatar




PM


Posts: 91
xx 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.
User IP Logged

Rod
Full Member
ImageImageImage


member is offline

Avatar




PM

Gender: Male
Posts: 110
xx 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.
User IP Logged

RobM
Junior Member
ImageImage


member is offline

Avatar




PM


Posts: 91
xx 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 smiley
User IP Logged

Richard Russell
Administrator
ImageImageImageImageImage


member is offline

Avatar




Homepage PM


Posts: 1348
xx 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 smiley

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.
« Last Edit: Oct 12th, 2016, 4:20pm by Richard Russell » User IP Logged

RobM
Junior Member
ImageImage


member is offline

Avatar




PM


Posts: 91
xx 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.
User IP Logged

Richard Russell
Administrator
ImageImageImageImageImage


member is offline

Avatar




Homepage PM


Posts: 1348
xx 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.
User IP Logged

RobM
Junior Member
ImageImage


member is offline

Avatar




PM


Posts: 91
xx 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.
User IP Logged

Richard Russell
Administrator
ImageImageImageImageImage


member is offline

Avatar




Homepage PM


Posts: 1348
xx 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.
« Last Edit: Oct 13th, 2016, 8:28pm by Richard Russell » User IP Logged

RobM
Junior Member
ImageImage


member is offline

Avatar




PM


Posts: 91
xx 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.
User IP Logged

RobM
Junior Member
ImageImage


member is offline

Avatar




PM


Posts: 91
xx 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 » User IP Logged

RobM
Junior Member
ImageImage


member is offline

Avatar




PM


Posts: 91
xx 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 » User IP Logged

Pages: 1  Notify Send Topic Print
« Previous Topic | Next Topic »

| |

This forum powered for FREE by Conforums ©
Terms of Service | Privacy Policy | Conforums Support | Parental Controls