Hello, everybody. Does anyone know of an easy way to enumerate, and change the audio device that playsound() uses. I want to ONLY redirect the audio that my PB program is playing and leave system sounds wherever they may be.
I am hoping that there will be an easy fix, and I wont have to go all API, so I don't have to rewrite a bunch.
Any help would be appreciated!
Enum / Change audio output device? (Windows)
-
- User
- Posts: 89
- Joined: Thu Feb 04, 2010 7:34 am
- Location: Decatur, TX
- Contact:
-
- User
- Posts: 89
- Joined: Thu Feb 04, 2010 7:34 am
- Location: Decatur, TX
- Contact:
Re: Enum / Change audio output device? (Windows)
Hello, Justin. I've been watching this thread, and since no one seems to have a quick and easy answer for you, I figured I'd give you some help. Here is the solution to your problem. A small library that for your purposes, works like the PB sound library, BUT it allows you to select an audio output device on a PER SOUND basis. Yes! It's your dream come true. I'm hope you find this library I made for you very helpful. And good luck on your future endeavors!
Code: Select all
Structure SOUND_DEVICE_LIST
szName.s
iDevID.i
nChannels.i
iMfgrID.i
iProdID.i
szInterface.s
EndStructure
Structure RIFF_HEADER
tag.c[4]
iSize.l
*pData.String
EndStructure
Structure PRIFF_HEADER
*lpRIFF.RIFF_HEADER
EndStructure
Structure SOUND_STRUCT
*pRawSound
dwRawDataSize.l
List pRiffs.PRIFF_HEADER()
bIsLoop.b
bIsPlaying.b
wavefmt.WAVEFORMATEX
whdr.WAVEHDR
hwo.i
nAudioDevice.i
iMonitorThread.i
iSignalByte.i
iSemaphore.i
EndStructure
Global NewList active_sounds.SOUND_STRUCT()
Global SoundMgrMutex = CreateMutex()
Global defWaveOutput = #WAVE_MAPPER
Declare Get_Sound_Devices( List sd.SOUND_DEVICE_LIST() )
Procedure is_sound( *soundNumber )
retVal = #False
LockMutex(SoundMgrMutex)
ForEach active_sounds()
If @active_sounds() = *soundNumber
retVal = #True
Break
EndIf
Next
UnlockMutex(SoundMgrMutex)
ProcedureReturn retVal
EndProcedure
Procedure Prep_Sound( *lpSoundStruct.SOUND_STRUCT )
retVal = #False
*ptr = *lpSoundStruct\pRawSound + 12
While Not(ok_to_stop)
*lpRIFF.RIFF_HEADER = *ptr
Select PeekS(@*lpRIFF\tag[0], 4)
Case "fmt "
CopyStructure(@*lpRIFF\pData, @*lpSoundStruct\wavefmt, WAVEFORMATEX)
*lpSoundStruct\wavefmt\cbSize = SizeOf(WAVEFORMATEX)
Case "data"
AddElement(*lpSoundStruct\pRiffs())
*lpSoundStruct\pRiffs()\lpRIFF = *lpRIFF
Case ""
ok_to_stop = #True
EndSelect
*ptr = @*lpRIFF\pData + *lpRIFF\iSize
Wend
If ListSize(*lpSoundStruct\pRiffs()) > 0
retVal = #True
EndIf
ProcedureReturn retVal
EndProcedure
Procedure Load_Sound( szFileName.s )
*retVal = 0
myFile = OpenFile(#PB_Any, szFileName)
If Not(IsFile(myFile))
ProcedureReturn *retVal
EndIf
myLof = Lof(myFile)
If mylof = 0
CloseFile(myFile)
ProcedureReturn *retVal
EndIf
LockMutex(SoundMgrMutex)
AddElement(active_sounds())
active_sounds()\pRawSound = AllocateMemory(mylof)
ReadData(myFile, active_sounds()\pRawSound, mylof)
CloseFile(myFile)
If Not(Prep_Sound(@active_sounds()))
FreeMemory(active_sounds()\pRawSound)
DeleteElement(active_sounds())
Else
active_sounds()\nAudioDevice = defWaveOutput
*retVal = @active_sounds()
EndIf
UnlockMutex(SoundMgrMutex)
ProcedureReturn *retVal
EndProcedure
Procedure Set_Default_Audio_Device( nDefaultDevice ) ; Returns previous audio device, or NULL on failure.
retVal = defWaveOutput
defWaveOutput = nDefaultDevice
ProcedureReturn retVal
EndProcedure
Procedure Is_Sound_Device_Good( nSoundDevice )
retVal = #False
NewList sd.SOUND_DEVICE_LIST()
If Get_Sound_Devices(sd()) > 0
ForEach sd()
If sd()\iDevID = nSoundDevice
retVal = #True
Break
EndIf
Next
EndIf
ProcedureReturn retVal
EndProcedure
Procedure Catch_Sound( *lpAddr )
EndProcedure
Procedure Free_Sound( *soundNumber )
If is_sound(*soundNumber)
LockMutex(SoundMgrMutex)
ChangeCurrentElement(active_sounds(), *soundNumber)
FreeMemory(active_sounds()\pRawSound)
ClearList(active_sounds()\pRiffs())
If active_sounds()\bIsLoop = #True
active_sounds()\iSignalByte = -1
SignalSemaphore(active_sounds()\iSemaphore)
EndIf
While IsThread(active_sounds()\iMonitorThread)
Delay(100)
Wend
DeleteElement(active_sounds())
UnlockMutex(SoundMgrMutex)
Else
ProcedureReturn #False
EndIf
ProcedureReturn #True
EndProcedure
Procedure Wave_Output_CallBack( hwo, uMsg, *lpSoundStruct.SOUND_STRUCT, dwParam1, dwParam2 )
Static whdr.WAVEHDR
Select uMsg
Case #WOM_OPEN
Case #WOM_DONE
If *lpSoundStruct\bIsPlaying = #True
If *lpSoundStruct\bIsLoop = #True
*lpSoundStruct\iSignalByte = 1
SignalSemaphore(*lpSoundStruct\iSemaphore)
EndIf
EndIf
Case #WOM_CLOSE
*lpSoundStruct\bIsPlaying = #False
EndSelect
EndProcedure
Procedure Is_Sound_Playing( *soundNumber )
If Not(is_sound(*soundNumber))
ProcedureReturn #False
EndIf
*lpSoundStruct.SOUND_STRUCT = *soundNumber
retVal = *lpSoundStruct\bIsPlaying
ProcedureReturn retVal
EndProcedure
Procedure stop_sound( *soundNumber )
If Not(is_sound(*soundNumber))
ProcedureReturn #False
EndIf
*lpSoundStruct.SOUND_STRUCT = *soundNumber
*lpSoundStruct\bIsPlaying = #False
waveOutReset_(*lpSoundStruct\hwo)
waveOutClose_(*lpSoundStruct\hwo)
If *lpSoundStruct\bIsLoop = #True
*lpSoundStruct\iSignalByte = -1
SignalSemaphore(*lpSoundStruct\iSemaphore)
EndIf
While IsThread(*lpSoundStruct\iMonitorThread)
Delay(100)
Wend
ProcedureReturn 0
EndProcedure
Procedure Monitor_Sound_Thread( *lpSoundStruct.SOUND_STRUCT )
While *lpSoundStruct\iSignalByte <> -1
WaitSemaphore(*lpSoundStruct\iSemaphore)
Select *lpSoundStruct\iSignalByte
Case 1
Debug "Repeat sound!"
Case -1
Break
EndSelect
Wend
ProcedureReturn #Null
EndProcedure
Procedure Play_Sound( *soundNumber, bLoopSound = #False, nSoundDevice = -1 ) ;Returns -1 for bad sound device.
retVal = #False
If Not(is_sound(*soundNumber))
ProcedureReturn retVal
EndIf
*lpSoundStruct.SOUND_STRUCT = *soundNumber
ChangeCurrentElement(active_sounds(), *soundNumber)
If nSoundDevice = -1
nSoundDevice = *lpSoundStruct\nAudioDevice
EndIf
If (nSoundDevice = #WAVE_MAPPER Or Is_Sound_Device_Good(nSoundDevice))
mmResult = waveOutOpen_(@*lpSoundStruct\hwo, nSoundDevice, @*lpSoundStruct\wavefmt, @Wave_Output_CallBack(), *soundNumber, #CALLBACK_FUNCTION)
If mmResult = #MMSYSERR_NOERROR
If bLoopSound = #True
*lpSoundStruct\bIsLoop = #True
*lpSoundStruct\iSignalByte = 0
*lpSoundStruct\iSemaphore = CreateSemaphore(0)
*lpSoundStruct\iMonitorThread = CreateThread(@Monitor_Sound_Thread(), *lpSoundStruct)
EndIf
If FirstElement(*lpSoundStruct\pRiffs())
*lpSoundStruct\whdr\dwBufferLength = *lpSoundStruct\pRiffs()\lpRIFF\iSize
*lpSoundStruct\whdr\lpData = @*lpSoundStruct\pRiffs()\lpRIFF\pData
waveOutPrepareHeader_(*lpSoundStruct\hwo, @*lpSoundStruct\whdr, SizeOf(WAVEHDR))
waveOutWrite_(*lpSoundStruct\hwo, @*lpSoundStruct\whdr, SizeOf(WAVEHDR))
*lpSoundStruct\bIsPlaying = #True
EndIf
retVal = #True
Else
Debug "Failed!"
EndIf
Else
retVal = -1
EndIf
ProcedureReturn retVal
EndProcedure
Enumeration
#VOLUME_MUTE = 0
#VOLUME_LOW = 50
#VOLUME_MID = $7FFF
#VOLUME_HIGH = 200
#VOLUME_LOUD = $FFFF
EndEnumeration
Procedure Get_Sound_Devices( List sd.SOUND_DEVICE_LIST() )
numDevs = waveOutGetNumDevs_()
ClearList(sd())
If numDevs > 0
For i = 0 To (numDevs - 1)
If waveOutGetDevCaps_(i, @woc.WAVEOUTCAPS, SizeOf(WAVEOUTCAPS)) = #MMSYSERR_NOERROR
AddElement(sd())
sd()\szName = PeekS(@woc\szPname[0])
sd()\iDevID = i
sd()\nChannels = woc\wChannels
sd()\iMfgrID = woc\wMid
sd()\iProdID = woc\wPid
mySize = 400
myValue.s = Space(mySize)
myString.s = ""
If waveOutMessage_(i, #DRV_RESERVED + 12, @myValue, mySize) = #MMSYSERR_NOERROR
;- Change unicode string to Ascii
For j = 0 To 399
mychar = PeekB(@myValue + j)
If mychar = 0
If PeekB(@myValue + j + 1) = 0
Break
EndIf
Else
myString.s + Chr(mychar)
EndIf
Next
EndIf
sd()\szInterface = myString
EndIf
Next
EndIf
ProcedureReturn ListSize(sd())
EndProcedure
Procedure set_sound_volume( *soundNumber, volume )
If Not(is_sound(*soundNumber))
ProcedureReturn #False
EndIf
*lpSoundStruct.SOUND_STRUCT = *soundNumber
iVolume = 0
waveOutGetVolume_(*lpSoundStruct\hwo, @iVolume)
waveOutSetVolume_(*lpSoundStruct\hwo, volume)
ProcedureReturn iVolume
EndProcedure
mySound = Load_Sound("c:\users\justin\desktop\dialtone.wav")
If is_sound(mySound)
OpenWindow(1, 0, 0, 500, 500, "Waiting", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
ButtonGadget(1, 10, 10, 100, 20, "Stop")
NewList sdevs.SOUND_DEVICE_LIST()
Play_Sound(mySound, #True, 1)
set_sound_volume(mySound, #VOLUME_LOUD)
Repeat
dwEvent = WaitWindowEvent()
Select dwEvent
Case #PB_Event_Gadget
If EventGadget() = 1
If EventType() = #PB_EventType_LeftClick
If Is_Sound_Playing(mySound)
stop_sound(mySound)
EndIf
EndIf
EndIf
EndSelect
Until dwEvent = #PB_Event_CloseWindow
CloseWindow(1)
If Is_Sound_Playing(mySound)
stop_sound(mySound)
EndIf
Free_Sound(mySound)
Else
Debug "Sound Bad!"
EndIf
-
- User
- Posts: 89
- Joined: Thu Feb 04, 2010 7:34 am
- Location: Decatur, TX
- Contact:
Re: Enum / Change audio output device? (Windows)
Why...THANK YOU JUSTIN! 

-
- User
- Posts: 89
- Joined: Thu Feb 04, 2010 7:34 am
- Location: Decatur, TX
- Contact:
Re: Enum / Change audio output device? (Windows)
By the way, I didn't spend a lot more time on this library before posting it on making sure it loops or splitting the RIFF 'data' into multiple buffers, or making my volume constants accurate for that matter, but if anyone wants to tinker with it and re-post for others, that would be great.
Re: Enum / Change audio output device? (Windows)
Hi:
I was looking for that also, thank you very much
I was looking for that also, thank you very much
PureBasic: Surprisingly simple, diabolically powerful
-
- User
- Posts: 89
- Joined: Thu Feb 04, 2010 7:34 am
- Location: Decatur, TX
- Contact:
Re: Enum / Change audio output device? (Windows)
Hey, man, NO problem. I'm glad it helps you!