Confused about SoundPlay and Wave volume Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
doubledave22
Posts: 343
Joined: 08 Jun 2019, 17:36

Confused about SoundPlay and Wave volume

Post by doubledave22 » 03 Jan 2022, 18:36

I am having trouble controlling the volume level for an individual soundplay command when playing a .wav file. The soundset has no effect here:

Code: Select all

^t::
tooltip, full volume
SoundPlay, C:\WINDOWS\Media\chimes.wav
sleep 2000 
tooltip, half volume
SoundSet, 50, WAVE
SoundPlay, C:\WINDOWS\Media\chimes.wav
return
I have tried many different DeviceNumbers and ran the soundcard analysis script to double check. It seems the output device number I'm using is 9 but errorlevel for soundset using that number returns: "Component Doesn't Support This Control Type".

I'm not sure what to do. Ideally I want to have a .wav play through a user's current output device and be able to control the individual volume of each .wav (and not have to manipulate system sound).

Does anyone know the best way to control individual sound volumes inside a script?

User avatar
mikeyww
Posts: 26942
Joined: 09 Sep 2014, 18:38

Re: Confused about SoundPlay and Wave volume

Post by mikeyww » 03 Jan 2022, 19:17

I'm not an expert in this, but one workaround is to get the volume, change it, play your file, and restore the volume (can use the MASTER default component type). My understanding is that "WAVE" does not refer to a file type, but rather the sound that is generated from digital waveform data sent from the CPU to the sound card. The second parameter of SoundSet is the component type, not the file type.

There are some other posts, I believe, with scripts to change the volume of specific devices, and perhaps programs (I did not try them).

doubledave22
Posts: 343
Joined: 08 Jun 2019, 17:36

Re: Confused about SoundPlay and Wave volume

Post by doubledave22 » 03 Jan 2022, 19:29

I see... I was hoping there would be another way to set the wav volumes individually. I now see the difference between component and filetype and WAVE and .wav not being the same in this case. Thanks for clearing that up.

I really don't want to manipulate MASTER like you suggested since if the user is also playing music, you can hear the spikes and dips when playing around with system volume.

I did find a way to control the script's own volume here: viewtopic.php?t=46654
Perhaps I can manipulate the scripts own output volume for each soundplay but this overall seems a bit hacky. I'm surprised there's not a straight-forward way to do this perhaps someone else may know.

User avatar
mikeyww
Posts: 26942
Joined: 09 Sep 2014, 18:38

Re: Confused about SoundPlay and Wave volume

Post by mikeyww » 03 Jan 2022, 19:33

If you are using a specific player (e.g., VLC), you could probably change the volume for that specific program (perhaps along the lines of the post that you cited). Others may have some fancier ideas here.

doubledave22
Posts: 343
Joined: 08 Jun 2019, 17:36

Re: Confused about SoundPlay and Wave volume

Post by doubledave22 » 03 Jan 2022, 19:44

mikeyww wrote:
03 Jan 2022, 19:33
If you are using a specific player (e.g., VLC), you could probably change the volume for that specific program (perhaps along the lines of the post that you cited). Others may have some fancier ideas here.
My own script is the one producing the sounds so no external players are being used here. I just want to be able to control the output volume of each soundplay command. I appreciate the help as always!

User avatar
mikeyww
Posts: 26942
Joined: 09 Sep 2014, 18:38

Re: Confused about SoundPlay and Wave volume

Post by mikeyww » 03 Jan 2022, 20:24

Code: Select all

Try Menu, Tray, Icon, %A_WinDir%\System32\shell32.dll, -277
Global wma1 := ComObjCreate("WMPlayer.OCX")
song = d:\mwsounds\endOfDisc-v02.wav
play(song, 25)
play(song)

play(song, volume := 100) { ; https://www.autohotkey.com/boards/viewtopic.php?p=81003#p81003
 ; https://www.autohotkey.com/boards/viewtopic.php?p=436997#p436997
 wma1.settings.Volume := volume
 wma1.url             := song
 ; wma1.controls.stop
 ; wma1.controls.play
 Sleep, 1000 * wma1.newMedia(song).getItemInfo("Duration")
}

teadrinker
Posts: 4331
Joined: 29 Mar 2015, 09:41
Contact:

Re: Confused about SoundPlay and Wave volume

Post by teadrinker » 03 Jan 2022, 21:18

As an option, the volume of the current process can be set as follows:

Code: Select all

soundFilePath := A_WinDir . "\MEDIA\tada.wav"

SetCurrentProcessVolume(10)
SoundPlay, % soundFilePath, Wait

SetCurrentProcessVolume(40)
SoundPlay, % soundFilePath, Wait

SetCurrentProcessVolume(100)
SoundPlay, % soundFilePath, Wait

SetCurrentProcessVolume(volume) ; volume can be number 0 — 100 or "mute" or "unmute"
{
   static MMDeviceEnumerator      := "{BCDE0395-E52F-467C-8E3D-C4579291692E}"
        , IID_IMMDeviceEnumerator := "{A95664D2-9614-4F35-A746-DE8DB63617E6}"
        , IID_IAudioClient        := "{1cb9ad4c-dbfa-4c32-b178-c2f568a703b2}"
        , IID_ISimpleAudioVolume  := "{87ce5498-68d6-44e5-9215-6da47ef883d8}"
        , eRender := 0, eMultimedia := 1, CLSCTX_ALL := 0x17
        , _ := OnExit( Func("SetCurrentProcessVolume").Bind(100) )
        
   IMMDeviceEnumerator := ComObjCreate(MMDeviceEnumerator, IID_IMMDeviceEnumerator)
   ; IMMDeviceEnumerator::GetDefaultAudioEndpoint
   DllCall(NumGet(NumGet(IMMDeviceEnumerator + 0) + A_PtrSize*4), "Ptr", IMMDeviceEnumerator, "UInt", eRender, "UInt", eMultimedia, "PtrP", IMMDevice)
   ObjRelease(IMMDeviceEnumerator)

   VarSetCapacity(GUID, 16)
   DllCall("Ole32\CLSIDFromString", "Str", IID_IAudioClient, "Ptr", &GUID)
   ; IMMDevice::Activate
   DllCall(NumGet(NumGet(IMMDevice + 0) + A_PtrSize*3), "Ptr", IMMDevice, "Ptr", &GUID, "UInt", CLSCTX_ALL, "Ptr", 0, "PtrP", IAudioClient)
   ObjRelease(IMMDevice)
   
   ; IAudioClient::GetMixFormat
   DllCall(NumGet(NumGet(IAudioClient + 0) + A_PtrSize*8), "Ptr", IAudioClient, "UIntP", pFormat)
   ; IAudioClient::Initialize
   DllCall(NumGet(NumGet(IAudioClient + 0) + A_PtrSize*3), "Ptr", IAudioClient, "UInt", 0, "UInt", 0, "UInt64", 0, "UInt64", 0, "Ptr", pFormat, "Ptr", 0)
   DllCall("Ole32\CLSIDFromString", "Str", IID_ISimpleAudioVolume, "Ptr", &GUID)
   ; IAudioClient::GetService
   DllCall(NumGet(NumGet(IAudioClient + 0) + A_PtrSize*14), "Ptr", IAudioClient, "Ptr", &GUID, "PtrP", ISimpleAudioVolume)
   ObjRelease(IAudioClient)
   if (volume + 0 != "")
      ; ISimpleAudioVolume::SetMasterVolume
      DllCall(NumGet(NumGet(ISimpleAudioVolume + 0) + A_PtrSize*3), "Ptr", ISimpleAudioVolume, "Float", volume/100, "Ptr", 0)
   else
      ; ISimpleAudioVolume::SetMute
      DllCall(NumGet(NumGet(ISimpleAudioVolume + 0) + A_PtrSize*5), "Ptr", ISimpleAudioVolume, "UInt", volume = "mute" ? true : false, "Ptr", 0)
   ObjRelease(ISimpleAudioVolume)
}

doubledave22
Posts: 343
Joined: 08 Jun 2019, 17:36

Re: Confused about SoundPlay and Wave volume

Post by doubledave22 » 04 Jan 2022, 11:48

@mikeyww Thanks this works! Do you know if this is supported/built into all Windows machines and versions (win 7 and above really)?

Also as long as we dont call wma1.openPlayer it will always just play in the background correct?

Any recommendations on initializing in case this fails or is this a total waste:

Code: Select all

global wma1
try wma1 := ComObjCreate("WMPlayer.OCX")
catch
	MsgBox Could not load WMPLayer
I would ideally want this done in the function itself but I'm not sure how to initialize static vars with try/catch

Code: Select all

Play(song, volume := 100) 
{
	static wma1 := ComObjCreate("WMPlayer.OCX")	; <--- How would I wrap this in a try/catch?

	wma1.settings.Volume := volume
	wma1.url             := song
	
	Sleep, 1000 * wma1.newMedia(song).getItemInfo("Duration")
}
Would it be like this?

Code: Select all


Play(song, volume := 100) 
{ ; https://www.autohotkey.com/boards/viewtopic.php?p=81003#p81003, https://www.autohotkey.com/boards/viewtopic.php?p=436997#p436997
	static wma1, _init
	
	if (!isobject(wma1) and !_init)
	{
		_init := 1
		try wma1 := ComObjCreate("WMPlayer.OCX")
		catch
		{
			msgbox, could not load WMPLayer
			return
		}
	}
	
	wma1.settings.Volume := volume
	wma1.url             := song
	
	Sleep, 1000 * wma1.newMedia(song).getItemInfo("Duration")
}
Any thanks to @teadrinker, this will be my backup plan if I cant get the WMPlayer to work

User avatar
mikeyww
Posts: 26942
Joined: 09 Sep 2014, 18:38

Re: Confused about SoundPlay and Wave volume

Post by mikeyww » 04 Jan 2022, 12:46

I believe you have it all right-- but your test will prove it. I doubt that you need the variable _init.

doubledave22
Posts: 343
Joined: 08 Jun 2019, 17:36

Re: Confused about SoundPlay and Wave volume

Post by doubledave22 » 04 Jan 2022, 14:29

It seems that wma1.newMedia(wav).getItemInfo("Duration") only returns seconds and if it's < 1 it shows 0. I'm not sure a great solution since 1s was too long for really short .wavs so I just am using .15s instead for my needs.

ended up with this. I am using the _init variable so in case ComObjCreate fails we don't keep trying. Open to ideas (also still not sure this ever fails).

Code: Select all

Play_Wav(wav, volume := 100)
{ ; https://www.autohotkey.com/boards/viewtopic.php?p=81003#p81003, https://www.autohotkey.com/boards/viewtopic.php?p=436997#p436997
	static wma1, _init
	
	critical, off       ; I have issues with critical threads and wma1 not playing back to back clips without critical off

	if (!_init)
	{
		_init := 1
		try wma1 := ComObjCreate("WMPlayer.OCX")
		catch
			msgbox, Windows Media Player could not be loaded
	}
	
	if (!isobject(wma1))
	{
		SoundPlay, % wav     ; use Wait to play full sound clip
		sleep, 100               ; otherwise let short (<100ms) clips finish but interrupt after
		return
	}
	
	wma1.settings.Volume := volume
	wma1.url             := wav
	
	Length := wma1.newMedia(wav).getItemInfo("Duration")
	
	if (!Length)
		Length := .15

	Sleep, 1000 * Length
}

User avatar
mikeyww
Posts: 26942
Joined: 09 Sep 2014, 18:38

Re: Confused about SoundPlay and Wave volume

Post by mikeyww » 04 Jan 2022, 15:17

I noticed that issue about the duration earlier, too. Your solution looks OK to me. If the duration is 4.25 seconds (or noted as 4 seconds, for example), perhaps you also want to add the 0.15, etc. (i.e., "round up" in a way). That is up to you. If you need more detailed information about the media duration, there are other ways to get it (e.g., programs to read metadata), but it might not be essential for you. I did not explore very much about how the duration is actually reported.

teadrinker
Posts: 4331
Joined: 29 Mar 2015, 09:41
Contact:

Re: Confused about SoundPlay and Wave volume

Post by teadrinker » 04 Jan 2022, 16:01

Try this:

Code: Select all

PlaySound(mediaFilePath, volume := 100) {
   static WMP := ComObjCreate("WMPlayer.OCX.7")
   WMP.Settings.Volume := volume
   WMP.url := mediaFilePath
   while WMP.PlayState != 1
      Sleep, 10
}

User avatar
mikeyww
Posts: 26942
Joined: 09 Sep 2014, 18:38

Re: Confused about SoundPlay and Wave volume

Post by mikeyww » 04 Jan 2022, 16:07

Very nice! Thank you, teadrinker.

doubledave22
Posts: 343
Joined: 08 Jun 2019, 17:36

Re: Confused about SoundPlay and Wave volume

Post by doubledave22 » 04 Jan 2022, 16:12

@teadrinker that looks clean! Thanks!!

Any compatibility issues with WMPlayer.OCX.7 I should know about? Is it supported on Win7+ natively?

teadrinker
Posts: 4331
Joined: 29 Mar 2015, 09:41
Contact:

Re: Confused about SoundPlay and Wave volume

Post by teadrinker » 04 Jan 2022, 16:37

WMPlayer.OCX.7 is the latest version of this object, I don't know what is the difference between it and WMPlayer.OCX. It is supported on Windows 7. Description is here.

doubledave22
Posts: 343
Joined: 08 Jun 2019, 17:36

Re: Confused about SoundPlay and Wave volume

Post by doubledave22 » 05 Jan 2022, 11:08

@teadrinker I have some questions about this implementation.
1) Is there any way to help the while loop break if the thread happens to be critical? Or do I just have to turn critical, off inside Play_Wav

Code: Select all

^t::
critical
Play_Wav(song)
return
Play_Wav(mediaFilePath, volume := 100) 
{
   static WMP := ComObjCreate("WMPlayer.OCX.7")
   
   WMP.Settings.Volume := volume
   WMP.url := mediaFilePath
   
   while WMP.PlayState != 1
   {
      Sleep, 10
      tooltip cant exit!
   }
}
2) Is the while WMP.PlayState != 1 designed to wait until the sound has played and then play the next sound? It works if multiple sounds are played on the same thread but it cuts itself off when a hotkey is pressed simultaneously.

This below works fine.

Code: Select all

^t::
song := A_WinDir . "\MEDIA\Alarm02.wav"
Play_Wav(song)
Play_Wav(song)
return

Hit Ctrl+t twice in a row and it does not work and it cuts itself off

Code: Select all

^t::
song := A_WinDir . "\MEDIA\Alarm02.wav"
Play_Wav(song)
return
So I made this modification. It waits for any previous sounds to finish first then plays.

Code: Select all

Play_Wav(mediaFilePath, volume := 100) 
{
   static WMP := ComObjCreate("WMPlayer.OCX.7")
   
	while WMP.PlayState = 3
      Sleep, 10
   
   WMP.Settings.Volume := volume
   WMP.url := mediaFilePath

}
The issue here is some sounds are lost. If I press Ctrl+t here 5 times quickly it will only play twice.

My solution was to use bind timers:

Code: Select all

song := A_WinDir . "\MEDIA\Alarm02.wav"
song2 := A_WinDir . "\MEDIA\ding.wav"

return

^t::
Play_Bind := func("Play_Wav").bind(song)
settimer, % Play_Bind, -1
return

^r::
Play_Bind2 := func("Play_Wav").bind(song2)
settimer, % Play_Bind2, -1
return

Play_Wav(mediaFilePath, volume := 100) 
{
   static WMP := ComObjCreate("WMPlayer.OCX.7")
   
	while WMP.PlayState = 3
      Sleep, 10
   
   WMP.Settings.Volume := volume
   WMP.url := mediaFilePath
   
   while WMP.PlayState != 1
      Sleep, 10
}

And it seemed that I had to have both while loops going in order to buffer each call to Play_Wav(). The issue with this solution is sometimes the calls go out of order. I am guessing they are racing each other inside the while loops.

Ultimately my goal is for no sounds to get lost and all sounds to play in order without cutting itself off.

doubledave22
Posts: 343
Joined: 08 Jun 2019, 17:36

Re: Confused about SoundPlay and Wave volume

Post by doubledave22 » 05 Jan 2022, 14:34

As a follow up, I did find this library: https://www.autohotkey.com/board/topic/51252-wrapper-for-bassdll-2450/

Which seems really smooth and has a lot of options but i can't seem to find a way to control volume levels for individual calls to BASS_Play() but it does allow multiple sounds at once which may actually be useful for me

doubledave22
Posts: 343
Joined: 08 Jun 2019, 17:36

Re: Confused about SoundPlay and Wave volume  Topic is solved

Post by doubledave22 » 05 Jan 2022, 14:55

I think I've worked it out. I had to search for "vol" instead of "volume" inside the wrapper. For anyone trying to do this in the future... get bass.dll from http://www.un4seen.com/

include the wrapper:

Code: Select all

; Wrapper for bass.dll (2.4.5.0)
; www.autohotkey.com/forum/topic55454.html
; for AHK 1.0.48.05
; by toralf
; Version 0.1 (2010-02-20)
; based on BASS Library	by k3ph 

; NEEDS:  bass.dll      www.un4seen

; ################################
; ##  List of public functions  ##
; ################################
; BASS_Load([DLLPath, Device, PlugIns]) ;loads BASS wrapper, must be called before any other function
; BASS_Free()                           ;frees BASS wrapper, must be called at end of script
; BASS_IsPlaying(hStream)               ;returns playback status: 0=Stopped, 1=Playing, 2=Stalled, 3=Paused
; BASS_Play([File])                     ;plays a file or restarts it when paused and no file is specified
;                                         (returns hStream on success, otherwise -1)
; BASS_Stop()                           ;stop playback of file (returns hStream="" on success, otherwise -1)
; BASS_Pause()                          ;toogles pause of playback of file (returns hStream on success, otherwise -1)
; BASS_Volume([Volume])                 ;sets or gets master volume: 0 (silent) to 1 (max)
; BASS_GetDevice(hStream)               ;returns device number, otherwise -1
; BASS_Seconds2Bytes(hStream,SecPos)    ;converts a position from seconds to bytes,
;                                         returns on Error negative value
; BASS_Bytes2Seconds(hStream,BytePos)   ;converts a position from bytes to seconds,
;                                         returns on Error negative value
; BASS_GetLen(hStream)                  ;returns playback length in bytes, returns on Error -1
; BASS_GetPos(hStream)                  ;returns playback position in bytes, returns on Error -1
; BASS_SetPos(hStream,BytePos)          ;sets playback position in bytes, returns 1, otherwise 0
; BASS_GetLevel(hStream, ByRef LeftLevel, ByRef RightLevel) ;returns level (peak amplitude) of a stream, returns on Error -1
; BASS_GetLevel(hStream)                ;returns level (peak amplitude 0-32768) of a stream, returns on Error -1 or 0 when stream is stalled
; BASS_IsSliding(hStream,Attrib)        ;returns 1 if attribute is sliding, otherwise 0, 
;                                         Attributes: 1=Freq, 2=Vol, 3=Pan, 4=EAXMix
; BASS_SetSliding(hStream,Attrib,NewValue,TimeMS)   ;set attribute from its current value to slide to new value in time period
; BASS_GetCPU()                         ;returns CPU usage of BASS
; BASS_GetVersion()                     ;returns version of BASS that is loaded
; BASS_ProgressMeter_Add([x, y, Width, Height, Color, Bgcolor, ControlID, GuiID])  ;adds a progress meter to a gui
; BASS_ProgressMeter_Update(hStream [, ControlID, GuiID])   ;updates a progress meter for a stream
; BASS_PluginLoad(File)                 ;loads a plugin of BASS
; BASS_PluginFree([hPlugin])            ;frees a plugin of BASS ("" or 0 = All Plugins)
; BASS_ErrorGetCode()                   ;Return the error message as code and description
;
; #################################
; ##  List of private functions  ##
; #################################
; BASS_InitFree([Modus, DLLPath, Device, Plugins])
; BASS_StreamFileToChannel(Modus [, File])
; BASS_ProgressMeter(GuiID, ControlID, WidthOrhStream, Height [, Color, Bgcolor, X, Y])


;set Include directory for plugins
#Include %A_ScriptDir%\

;include effects & handlers
#Include *i basstags.ahk
#Include *i bassfx.ahk
#Include *i bassvfx.ahk
#Include *i bassenc.ahk
#Include *i bassmix.ahk
#Include *i bassvis.ahk
#Include *i bassvst.ahk
#Include *i basswadsp.ahk
#Include *i bassvideo.ahk

;include format plugins
#Include *i basscd.ahk
#Include *i bassmidi.ahk
#Include *i bassflac.ahk
#Include *i basswma.ahk
#Include *i basswv.ahk
#Include *i bassaac.ahk
#Include *i bassape.ahk
#Include *i bassmpc.ahk
#Include *i bassac3.ahk
#Include *i bassalac.ahk
#Include *i bassspx.ahk
#Include *i basstta.ahk
#Include *i bassofr.ahk

BASS_Load(DLLPath="", Device=-1, PlugIns="ALL"){  ;loads BASS wrapper, must be called before any other function
  Return BASS_InitFree("Load", DLLPath, Device, Plugins)
}
BASS_Free(){                             ;frees BASS wrapper, must be called at end of script 
  Return BASS_InitFree("Free")
}
BASS_InitFree(Modus="", DLLPath="", Device="", Plugins=""){
  static
  DLLName = bass.dll
  If (Modus = "Load"){                             ;load dll to memory
    If (!hBassDll := DllCall("LoadLibrary", Str,DLLPath . DLLName)){
      MsgBox, 48, BASS error!, 
        (Ltrim
          Failed to start %DLLName%
          Please ensure you have %DLLName% in the correct path %DLLPath%
        )
    	Return 0
    }
    local Freq=44100,Flags=0,Win=0,Clsid=0         ;initialize output device 
    If !DllCall("BASS\BASS_Init", Int,Device, Int,Freq, Int,Flags, UInt,Win, UInt,Clsid){
      ErrorLevel := BASS_ErrorGetCode()
      MsgBox, 48, BASS error!,
        (Ltrim
          Failed to initialize BASS
          Error: %ErrorLevel%
        )
      Return 0 
    }
    ;Load plugins
    PlugIns := PlugIns = "All"
            ? "tags,fx,vfx,enc,mix,vis,vst,wadsp,video,cd,midi,flac,wma,wv,aac,ape,mpc,ac3,alac,spx,tta,ofr"
            : PlugIns
    Loop, Parse, PlugIns, `,
      If IsFunc("BASS_" A_LoopField "_Load")
        BASS_%A_LoopField%_Load(DLLPath)
  }Else If (Modus = "Free"){                       
    If !BASS_PluginFree()                          ;free all Plugins
      MsgBox, 48, BASS error!, Failed to free all plugins.`nError: %ErrorLevel%
    DllCall("BASS\BASS_Free")                      ;free all resources used by the output device
    DllCall("FreeLibrary", UInt,hBassDll)          ;free dll from memory
  }
  Return 1
}

BASS_IsPlaying(hStream){ ;returns playback status: 0=Stopped, 1=Playing, 2=Stalled, 3=Paused
;BASS_ChannelIsActive return values
; BASS_ACTIVE_STOPPED := 0 ;
; BASS_ACTIVE_PLAYING := 1 ;
; BASS_ACTIVE_STALLED := 2 ; Playback of the stream has been stalled due to a lack of sample data. The playback will automatically resume once there is sufficient data to do so.
; BASS_ACTIVE_PAUSED  := 3 ;
  Return DllCall("BASS\BASS_ChannelIsActive", UInt,hStream)
}

BASS_Play(File=""){  ;plays a file or restarts it when paused and no file is specified (returns hStream on success, otherwise -1)         
  Return BASS_StreamFileToChannel("Play",File)
}
BASS_Stop(){         ;stop playback of file (returns hStream="" on success, otherwise -1)               
  Return BASS_StreamFileToChannel("Stop")
}
BASS_Pause(){        ;toogles pause of playback of file (returns hStream on success, otherwise -1)
  Return BASS_StreamFileToChannel("Pause")
}
BASS_StreamFileToChannel(Modus,File=""){
  static
  If (File AND !FileExist(File)){     ;check if file exists when specified
    MsgBox, 48, BASS error!,
      (Ltrim
        File does not exist:
        %File%
      )
    Return -1    
  }
  If (Modus = "Play" And File){      ;play file from beginning
  	If !(hStream := DllCall("BASS\BASS_StreamCreateFile", UInt,FromMem:=0
                           , UInt,&File, UInt64,Offset:=0, UInt64,Length:=0
                           , UInt,(A_IsUnicode ? 0x80000000 : 0x40000))){
      ErrorLevel := BASS_ErrorGetCode()
      MsgBox, 48, BASS error!,
        (Ltrim
          Failed to create a stream from file:
          %File%
          Error: %ErrorLevel%
        )
      Return -1
    }
    If !DllCall("BASS\BASS_ChannelPlay", UInt,hStream, Int,Restart:=1){
      ErrorLevel := BASS_ErrorGetCode()
      MsgBox, 48, BASS error!,
        (Ltrim
          Failed to play stream from file:
          %File%
          Error: %ErrorLevel%
        )
      Return -1      
    }
  }Else If (Modus = "Play" And !File And hStream){   ;restart playback (when paused)
    If !DllCall("BASS\BASS_ChannelPlay", UInt,hStream, Int,Restart:=0){
      ErrorLevel := BASS_ErrorGetCode()
      MsgBox, 48, BASS error!,
        (Ltrim
          Failed to restart stream from file:
          %File%
          Error: %ErrorLevel%
        )
      Return -1      
    }
  }Else If (Modus = "Stop" And hStream){             ;stop playback
    If BASS_IsPlaying(hStream)
      If !DllCall("BASS\BASS_ChannelStop", UInt,hStream){
        ErrorLevel := BASS_ErrorGetCode()
        MsgBox, 48, BASS error!,
          (Ltrim
            Failed to stop stream from file:
            %File%
            Error: %ErrorLevel%
          )
        Return -1      
      }
    hStream =                                        ;clear hStream
  }Else If (Modus = "Pause" And hStream){            ;toogle pause of playback
    local IsPlaying
    IsPlaying := BASS_IsPlaying(hStream)               ;get status
    If (IsPlaying = 3)                                    ;stream is paused
      hStream := BASS_Play()                                 ;restart playback
    Else If (IsPlaying = 1){                              ;stream is playing
      If !DllCall("BASS\BASS_ChannelPause", UInt,hStream){   ;pause playback
        ErrorLevel := BASS_ErrorGetCode()
        MsgBox, 48, BASS error!,
          (Ltrim
            Failed to pause stream from file:
            %File%
            Error: %ErrorLevel%
          )
        Return -1
      }
    }
  }
  Return hStream
}

BASS_Volume(Volume=""){ ;sets or gets master volume: 0 (silent) to 1 (max)  
	If Volume is float
    Return DllCall("BASS\BASS_SetVolume", Float,Volume)
  Else
	  Return DllCall("BASS\BASS_GetVolume", Float)
}
BASS_GetDevice(hStream){  ;returns device number, otherwise -1
	Return DllCall("BASS\BASS_ChannelGetDevice", UInt,hStream)
}

BASS_Seconds2Bytes(hStream,SecPos){  ;converts a position from seconds to bytes, returns on Error negative value
	Return DllCall("BASS\BASS_ChannelSeconds2Bytes", UInt,hStream, Double,SecPos, UInt64)
}
BASS_Bytes2Seconds(hStream,BytePos){ ;converts a position from bytes to seconds, returns on Error negative value
	Return DllCall("BASS\BASS_ChannelBytes2Seconds", UInt,hStream, UInt64,BytePos, Double)
}
BASS_GetLen(hStream){        ;returns playback length in bytes, returns on Error -1
	Return DllCall("BASS\BASS_ChannelGetLength", UInt,hStream, UInt,BASS_POS_BYTE := 0, UInt64)
}             
BASS_GetPos(hStream){      ;returns playback position in bytes, returns on Error -1
	Return DllCall("BASS\BASS_ChannelGetPosition", UInt,hStream, UInt,BASS_POS_BYTE := 0, UInt64)
}
BASS_SetPos(hStream,BytePos){   ;sets playback position in bytes, returns 1, otherwise 0
	Return DllCall("BASS\BASS_ChannelSetPosition", UInt,hStream, UInt64,Bytepos, UInt,BASS_POS_BYTE := 0)
}

BASS_GetLevel(WidthOrhStream, Byref LeftLevel,Byref RightLevel){ ;returns level (peak amplitude 0-32768) of a stream, returns on Error -1 or 0 when stream is stalled
	If ((LevelDWord := DllCall("BASS\BASS_ChannelGetLevel", UInt,hStream)) = -1)
    ErrorLevel := BASS_ErrorGetCode()
  If (LevelDWord > 0) {                       ;the level of the ...
    LeftLevel := LevelDWord & 0xffff            ;left channel is returned in the low word (low 16-bits)
    RightLevel := (LevelDWord>>16) & 0xffff     ;right channel is returned in the high word (high 16-bits)
  }Else{
    LeftLevel = 0
    RightLevel = 0
  }
  ;Return LevelDWord
  Return LeftLevel
}

BASS_IsSliding(hStream,Attrib){ ;returns 1 if attribute is sliding, otherwise 0, Attributes: 1=Freq, 2=Vol, 3=Pan, 4=EAXMix
;   BASS_ATTRIB_FREQ                := 1 ; 100 (min) to 100000 (max), 0 = original rate 
;   BASS_ATTRIB_VOL                 := 2 ; 0 (silent) to 1 (full).
;   BASS_ATTRIB_PAN                 := 3 ; -1 (full left) to +1 (full right), 0 = centre
;   BASS_ATTRIB_EAXMIX              := 4 ; 0 (full dry) to 1 (full wet), -1 = automatically calculate the mix based on the distance (the default).
  If Attrib is not number
  {
    If InStr(Attrib,"Freq")
      Attrib = 1
    Else If InStr(Attrib,"Vol")
      Attrib = 2
    Else If InStr(Attrib,"Pan")
      Attrib = 3
    Else If InStr(Attrib,"EAX")
      Attrib = 4
  }
  If Attrib not between 1 and 4
    Return 0
	Return DllCall("BASS\BASS_ChannelIsSliding", UInt,hStream, UInt,Attrib)
}
BASS_SetSliding(hStream,Attrib,NewValue,TimeMS){ ;set attribute from its current value to slide to new value in time period
;   BASS_ATTRIB_FREQ                := 1 ; 100 (min) to 100000 (max), 0 = original rate 
;   BASS_ATTRIB_VOL                 := 2 ; 0 (silent) to 1 (full).
;   BASS_ATTRIB_PAN                 := 3 ; -1 (full left) to +1 (full right), 0 = centre
;   BASS_ATTRIB_EAXMIX              := 4 ; 0 (full dry) to 1 (full wet), -1 = automatically calculate the mix based on the distance (the default).
  If Attrib is not number
  {
    If InStr(Attrib,"Freq")
      Attrib = 1
    Else If InStr(Attrib,"Vol")
      Attrib = 2
    Else If InStr(Attrib,"Pan")
      Attrib = 3
    Else If InStr(Attrib,"EAX")
      Attrib = 4
  }
  If Attrib not between 1 and 4
    Return 0
  If NewValue is not float
    Return 0
  If TimeMS is not number
    Return 0
	Return DllCall("BASS\BASS_ChannelSlideAttribute", UInt,hStream, UInt,Attrib, Float,NewValue, UInt,TimeMS)
}

BASS_GetCPU(){      ;returns CPU usage of BASS
	Return DllCall("BASS\BASS_GetCPU", Float)
}
BASS_GetVersion(){  ;returns version of BASS that is loaded
	Return DllCall("BASS\BASS_GetVersion")
}

; BASS_ProgressMeter
; based on idea by zed gecko  (StreamGUI, which comes with bass lib)
; adds a progress meter to a gui
BASS_ProgressMeter_Add(x = "", y = "", Width=300, Height=100, Color="green", Bgcolor="gray", ControlID="", GuiID=1){
  Return BASS_ProgressMeter(GuiID, ControlID, Width, Height//2, Color, Bgcolor, X, Y)
}
; updates a progress meter for a stream
BASS_ProgressMeter_Update(hStream, ControlID="", GuiID=1){
  Return BASS_ProgressMeter(GuiID, ControlID, hStream, "???????????")
}
BASS_ProgressMeter(GuiID, ControlID, WidthOrhStream, Height, Color="", Bgcolor="", X="", Y=""){
  global 
  local  SegmentSpace,SegmentSize,TransferVar,PrevSegment,NOS,LeftLevel,RightLevel
  If (Height = "???????????"){
    SetControlDelay, -1
    Loop, % NOS := NumberOfProgressMeterSegments%ControlID% {
      PrevSegment := A_Index - 1
      GuiControlGet, TransferVar, %GuiID%:, ProgressMeterL%ControlID%%A_Index%
      GuiControl,%GuiID%:, ProgressMeterL%ControlID%%PrevSegment%, %TransferVar%
      GuiControlGet, TransferVar, %GuiID%:, ProgressMeterR%ControlID%%A_Index%
      GuiControl,%GuiID%:, ProgressMeterR%ControlID%%PrevSegment%, %TransferVar%
    }
    BASS_GetLevel(WidthOrhStream, LeftLevel, RightLevel)
    GuiControl,%GuiID%:, ProgressMeterL%ControlID%%NOS%, %LeftLevel%
    GuiControl,%GuiID%:, ProgressMeterR%ControlID%%NOS%, % 32768 - RightLevel 
  }Else{
    SegmentSpace = 2                    ;visible size of one segment in pixel
    SegmentSize := SegmentSpace + 1
    NumberOfProgressMeterSegments%ControlID% := (WidthOrhStream // SegmentSize) - 1
    Gui, %GuiID%:Add, Progress, Range0-32768 vProgressMeterL%ControlID%0 w%SegmentSize% h%Height% %X% %Y% c%Color% Background%Bgcolor% Vertical,
    Gui, %GuiID%:Add, Progress, Range0-32768 vProgressMeterR%ControlID%0 w%SegmentSize% h%Height% y+0 c%Bgcolor% Background%Color% Vertical, 32769
    Loop, % NumberOfProgressMeterSegments%ControlID%{
      Gui, %GuiID%:Add, Progress, Range0-32768 vProgressMeterL%ControlID%%A_Index% w%SegmentSize% h%Height% xp+%SegmentSpace% yp-%Height% c%Color% Background%Bgcolor% Vertical,
      Gui, %GuiID%:Add, Progress, Range0-32768 vProgressMeterR%ControlID%%A_Index% w%SegmentSize% h%Height% y+0 c%Bgcolor% Background%Color% Vertical, 32769 
    }
  }
  Return 1
}

BASS_PluginLoad(File){   ;loads a plugin of BASS
;With any combination of these flags
;   BASS_UNICODE = 0x80000000 ;file is unicode
  Flags = 0
  If !hPlugin := DllCall("BASS\BASS_PluginLoad", UInt,&File, UInt,Flags)
    ErrorLevel := BASS_ErrorGetCode()
  Return hPlugin
}
BASS_PluginFree(hPlugin=0){  ;frees a plugin of BASS (0 = All Plugins)
	If !(Ret := DllCall("BASS\BASS_PluginFree", UInt,hPlugin))
    ErrorLevel := BASS_ErrorGetCode()
  Return Ret 
}

BASS_ErrorGetCode(){ ;Return the error message as code and description 
  static ErrorCodes :=
    (LTrim
      ";error codes returned by BASS_ErrorGetCode
      0  BASS_OK             all is OK
      1  BASS_ERROR_MEM      memory error
      2  BASS_ERROR_FILEOPEN can't open the file
      3  BASS_ERROR_DRIVER   can't find a free sound driver
      4  BASS_ERROR_BUFLOST  the sample buffer was lost
      5  BASS_ERROR_HANDLE   invalid handle
      6  BASS_ERROR_FORMAT   unsupported sample format
      7  BASS_ERROR_POSITION invalid position
      8  BASS_ERROR_INIT     BASS_Init has not been successfully called
      9  BASS_ERROR_START    BASS_Start has not been successfully called
      14 BASS_ERROR_ALREADY  already initialized/paused/whatever
      18 BASS_ERROR_NOCHAN   can't get a free channel
      19 BASS_ERROR_ILLTYPE  an illegal type was specified
      20 BASS_ERROR_ILLPARAM an illegal parameter was specified
      21 BASS_ERROR_NO3D     no 3D support
      22 BASS_ERROR_NOEAX    no EAX support
      23 BASS_ERROR_DEVICE   illegal device number
      24 BASS_ERROR_NOPLAY   not playing
      25 BASS_ERROR_FREQ     illegal sample rate
      27 BASS_ERROR_NOTFILE  the stream is not a file stream
      29 BASS_ERROR_NOHW     no hardware voices available
      31 BASS_ERROR_EMPTY    the MOD music has no sequence data
      32 BASS_ERROR_NONET    no internet connection could be opened
      33 BASS_ERROR_CREATE   couldn't create the file
      34 BASS_ERROR_NOFX     effects are not enabled
      37 BASS_ERROR_NOTAVAIL requested data is not available
      38 BASS_ERROR_DECODE   the channel is a decoding channel
      39 BASS_ERROR_DX       a sufficient DirectX version is not installed
      40 BASS_ERROR_TIMEOUT  connection timedout
      41 BASS_ERROR_FILEFORM unsupported file format
      42 BASS_ERROR_SPEAKER  unavailable speaker
      43 BASS_ERROR_VERSION  invalid BASS version (used by add-ons)
      44 BASS_ERROR_CODEC    codec is not available/supported
      45 BASS_ERROR_ENDED    the channel/file has ended
      -1 BASS_ERROR_UNKNOWN  some other mystery problem"
    )  
  Error := DllCall("BASS\BASS_ErrorGetCode", Int)
  Needle := "m`n)^" Error " +\w*\s*(?P<Value>.*)$"
  RegExMatch(ErrorCodes, Needle, Return)
  Return Error " = " ReturnValue
}
Then you can test it like this:

Code: Select all

#SingleInstance force
onexit bye

#include Bass.ahk ; save wrapper as Bass.ahk and make sure bass.dll is in same folder
Bass_load()
return

esc::
bye:
BASS_Free() 
exitapp

^t::
file1=C:\Windows\media\ding.wav
hStream := BASS_Play(file1) 		;Starts playback
BASS_SetSliding(hStream,2,.25,0)	; set volume to 25%

sleep, 1000
hStream := BASS_Play(file1) 		;Starts playback
BASS_SetSliding(hStream,2,1,0)		; set volume to 1

; Not implemented... build a "wait" feature to avoid overlapping sounds
; BASS_IsPlaying(hStream) 

return

teadrinker
Posts: 4331
Joined: 29 Mar 2015, 09:41
Contact:

Re: Confused about SoundPlay and Wave volume

Post by teadrinker » 05 Jan 2022, 15:11

You could try this way:

Code: Select all

SongArray := []

^t::
Critical
SongArray.Push({song: A_WinDir . "\MEDIA\Alarm02.wav", volume: 100})
timer := Func("Play_Wav").Bind(SongArray)
SetTimer, % timer, -10
return

Play_Wav(SongArray) 
{
   static WMP := ComObjCreate("WMPlayer.OCX.7")
   if WMP.PlayState = 3
      Return
   while SongArray.Count() {
      item := SongArray.RemoveAt(1)
      WMP.Settings.Volume := item.volume
      WMP.url := item.song
      while WMP.PlayState != 1
         Sleep, 10
   }
}


Post Reply

Return to “Ask for Help (v1)”