Page 1 of 2

PlaySound with a 20KHz file produces sound ?!

Posted: Fri Jan 09, 2026 7:59 am
by escape75
I'm playing around with a 20000Hz wave file that should not be heard (and it's not heard when using Windows Media Player)
but when I play back from PureBasic it produces a sound that I believe is around 8KHz more or less ...

InitSound()
CatchSound(#Sound, ?Sound)
PlaySound(#Sound)

What am I doing wrong ...

Re: PlaySound with a 20KHz file produces sound ?!

Posted: Fri Jan 09, 2026 9:39 am
by Skipper
Are these three lines all the code you're using?
Can you upload the WAV-file here?
What platform are you on and which version of PureBasic are you using?

cheers
Skipper

Re: PlaySound with a 20KHz file produces sound ?!

Posted: Sat Jan 10, 2026 1:00 am
by escape75
Here's the complete code:

Code: Select all



EnableExplicit

Enumeration 100
  #Window
  #Timer
  #Sound
EndEnumeration

OpenWindow(#Window, 0, 0, 300, 200, "20000Hz Sound", #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)

If InitSound()
  CatchSound(#Sound, ?Sound)
  PlaySound(#Sound)
  AddWindowTimer(#Window, #Timer, 5000)
EndIf

Repeat

  Define Event = WaitWindowEvent(250)

  If Event = #PB_Event_Timer And EventTimer() = #Timer
    PlaySound(#Sound)
  EndIf
  
Until Event = #PB_Event_CloseWindow

DataSection
  Sound:
  IncludeBinary "20000.wav"
EndDataSection

It's Windows 11 64bit with PureBasic 6.21

I'm including the link to the wave file below:
https://gofile.io/d/Koc4zZ

Thanks!

Re: PlaySound with a 20KHz file produces sound ?!

Posted: Sat Jan 10, 2026 1:47 am
by idle
without wav, I can still hear lower frequencies though?

Code: Select all

EnableExplicit 
InitSound() 

Structure RIFFStructure
  Riff.a[4]
  Length.l
  Wave.a[4]
EndStructure

Structure fmtStructure
  fmt.a[4]
  Length.l
  Format.u
  Channels.u
  SampleRate.l
  BytesPerSecond.l
  BlockAlign.u
  BitsPerSample.u
EndStructure

Structure dataStructure
  Signature.a[4]
  Length.l
EndStructure

Procedure.i CreateSound(Sound.i, Duration.i, Bits.i=16, Channels.i=2, SamplingRate.i=44100,frequency=500)
  
  Protected.i Result, HeaderSize, DataSize, samples,sample,time.f,tone.u  
  Protected *WAVBuffer, *RiffPtr.RIFFStructure, *fmtPtr.fmtStructure, *dataPtr.dataStructure, *audioPtr.Unicode
    
  HeaderSize = SizeOf(RIFFStructure)
  HeaderSize + SizeOf(fmtStructure)
  HeaderSize + SizeOf(dataStructure)
  
  DataSize = (Bits / 8) * SamplingRate * Duration * Channels / 1000
  samples = (DataSize / 2 / 2) - 1  
  
  *WAVBuffer = AllocateMemory(HeaderSize + DataSize, #PB_Memory_NoClear)
  If *WAVBuffer
    
    *RiffPtr = *WAVBuffer
    PokeS(@*RiffPtr\Riff, "RIFF", 4, #PB_Ascii|#PB_String_NoZero)
    *RiffPtr\Length = HeaderSize + DataSize - 8
    PokeS(@*RiffPtr\Wave, "WAVE", 4, #PB_Ascii|#PB_String_NoZero)
    
    *fmtPtr = *WAVBuffer + SizeOf(RIFFStructure)
    PokeS(@*fmtPtr\fmt, "fmt ", 4, #PB_Ascii|#PB_String_NoZero)
    *fmtPtr\Length = SizeOf(fmtStructure) - 8
    *fmtPtr\Format = 1
    *fmtPtr\Channels = Channels
    *fmtPtr\SampleRate = SamplingRate
    *fmtPtr\BitsPerSample = Bits
    *fmtPtr\BlockAlign = *fmtPtr\Channels * ((*fmtPtr\BitsPerSample + 7) / 8)
    *fmtPtr\BytesPerSecond = *fmtPtr\SampleRate * *fmtPtr\BlockAlign
    
    *dataPtr = *WAVBuffer + SizeOf(RIFFStructure) + SizeOf(fmtStructure)
    PokeS(@*dataPtr\Signature, "data", 4, #PB_Ascii|#PB_String_NoZero)
    *dataPtr\Length = DataSize
    
    *audioPtr = *WAVBuffer + SizeOf(RIFFStructure) + SizeOf(fmtStructure) + SizeOf(dataStructure) 
         
    For sample = 0 To samples 
	    time = sample / SamplingRate * 8
	    tone = Sin(time * frequency) * 32767
	    *audioPtr\u= tone  
	    *audioPtr+2 
	    *audioPtr\u = tone 
	    *audioPtr+2 
    Next
 
    Result = CatchSound(Sound, *WAVBuffer)
    FreeMemory(*WAVBuffer)
  EndIf
  
  ProcedureReturn Result
  
EndProcedure

 CreateSound(0,1000,16,2,44100,20000) 
 PlaySound(0) 
 Delay(1000)

Re: PlaySound with a 20KHz file produces sound ?!

Posted: Sat Jan 10, 2026 1:52 am
by escape75
Interesting, thanks!!

Does the above also produce an audible sound for you? ... It does for me ...

At 20KHz it should be silent :)

Re: PlaySound with a 20KHz file produces sound ?!

Posted: Sat Jan 10, 2026 2:01 am
by idle
Yes it does sound like it's aliasing. You need twice the sampling rate to play back a sound so it should work.
try with 96k

Code: Select all

CreateSound(0,1000,16,2,96000,20000) 
 PlaySound(0) 
 Delay(1000)
edit
testing it in my morse decoder at 20k it works and that uses a Goertzel function which is centered around 20k I can't hear it but it decodes the message.

Re: PlaySound with a 20KHz file produces sound ?!

Posted: Sat Jan 10, 2026 6:04 am
by escape75
When I do:

CreateSound(0,1000,16,2,96000,20000)

I can still hear it a little, so it's not 20KHz for sure, hmm!

--------------

It's interesting that this also keeps changing the playback every few seconds, like it keeps adding in more frequencies:

CreateSound(0,10000,16,2,96000,20000)
PlaySound(0)
Delay(10000)

Re: PlaySound with a 20KHz file produces sound ?!

Posted: Sat Jan 10, 2026 10:56 am
by infratec
You need an oszilloscope to check it.

If it is not 100% sine wave, you can hear the generated subfrequencies.

Re: PlaySound with a 20KHz file produces sound ?!

Posted: Sat Jan 10, 2026 9:36 pm
by Skipper
infratec wrote: Sat Jan 10, 2026 10:56 am You need an oszilloscope to check it.

If it is not 100% sine wave, you can hear the generated subfrequencies.
Yes, I agree. I also agree with Idle about sampling rate requirements. Search for "Nyquist frequency".

Re: PlaySound with a 20KHz file produces sound ?!

Posted: Sat Jan 10, 2026 10:04 pm
by idle
It was aliasing, the problem was the calculation for the tone, changing time to a double fixed it.

Code: Select all

EnableExplicit 
InitSound() 

Structure RIFFStructure
  Riff.a[4]
  Length.l
  Wave.a[4]
EndStructure

Structure fmtStructure
  fmt.a[4]
  Length.l
  Format.u
  Channels.u
  SampleRate.l
  BytesPerSecond.l
  BlockAlign.u
  BitsPerSample.u
EndStructure

Structure dataStructure
  Signature.a[4]
  Length.l
EndStructure

Procedure.i CreateSound(Sound.i, Duration.i, Bits.i=16, Channels.i=2, SamplingRate.i=44100,frequency=500)
  
  Protected.i Result, HeaderSize, DataSize, samples,sample,time.d,tone.u  
  Protected *WAVBuffer, *RiffPtr.RIFFStructure, *fmtPtr.fmtStructure, *dataPtr.dataStructure, *audioPtr.Unicode
    
  HeaderSize = SizeOf(RIFFStructure)
  HeaderSize + SizeOf(fmtStructure)
  HeaderSize + SizeOf(dataStructure)
  
  DataSize = (Bits / 8) * SamplingRate * Duration * Channels / 1000
  samples = (DataSize / 2 / 2) - 1  
  
  *WAVBuffer = AllocateMemory(HeaderSize + DataSize, #PB_Memory_NoClear)
  If *WAVBuffer
    
    *RiffPtr = *WAVBuffer
    PokeS(@*RiffPtr\Riff, "RIFF", 4, #PB_Ascii|#PB_String_NoZero)
    *RiffPtr\Length = HeaderSize + DataSize - 8
    PokeS(@*RiffPtr\Wave, "WAVE", 4, #PB_Ascii|#PB_String_NoZero)
    
    *fmtPtr = *WAVBuffer + SizeOf(RIFFStructure)
    PokeS(@*fmtPtr\fmt, "fmt ", 4, #PB_Ascii|#PB_String_NoZero)
    *fmtPtr\Length = SizeOf(fmtStructure) - 8
    *fmtPtr\Format = 1
    *fmtPtr\Channels = Channels
    *fmtPtr\SampleRate = SamplingRate
    *fmtPtr\BitsPerSample = Bits
    *fmtPtr\BlockAlign = *fmtPtr\Channels * ((*fmtPtr\BitsPerSample + 7) / 8)
    *fmtPtr\BytesPerSecond = *fmtPtr\SampleRate * *fmtPtr\BlockAlign
    
    *dataPtr = *WAVBuffer + SizeOf(RIFFStructure) + SizeOf(fmtStructure)
    PokeS(@*dataPtr\Signature, "data", 4, #PB_Ascii|#PB_String_NoZero)
    *dataPtr\Length = DataSize
    
    *audioPtr = *WAVBuffer + SizeOf(RIFFStructure) + SizeOf(fmtStructure) + SizeOf(dataStructure) 
         
    For sample = 0 To samples 
	    time = sample / SamplingRate * 8
	    tone = Sin(time * frequency) * 32767
	    *audioPtr\u= tone  
	    *audioPtr+2 
	    *audioPtr\u = tone 
	    *audioPtr+2 
    Next
 
    Result = CatchSound(Sound, *WAVBuffer)
    FreeMemory(*WAVBuffer)
  EndIf
  
  ProcedureReturn Result
  
EndProcedure

 CreateSound(0,10000,16,2,96000,20000) 
 PlaySound(0) 
 Delay(10000)

Re: PlaySound with a 20KHz file produces sound ?!

Posted: Sat Jan 10, 2026 10:08 pm
by escape75
I'm less so concerned with how accurate the sound playback is, although clearly there's some re-samplig going on here ...

But what I'm trying to do is send a 20KHz sound to speakers to wake them up, I found and modified the following code, but I feel like there's a better way of doing this and compressing the code further:

Code: Select all


EnableExplicit

Define T,i, *P, *OutBufMem

Global Volume.d = 0.00002
Global Up = #True

Enumeration 100
  #Window
EndEnumeration

Declare WinCallback(hwnd, uMsg, wParam, lParam)
Declare MAKE_WAVE(*SBuf)

Global SampleClock = 44100
Global BlockSize = 8192
Global BytesPerSample = 2
Global Channels = 2
Global nBuf = 8
Global Frequency = 2000
Global hWaveOut
Global NumOutDevs
Global PlayFormat.WAVEFORMATEX
Global Dim outHdr.WAVEHDR(nBuf)

Procedure WinCallback(hwnd, uMsg, wParam, lParam)
  
  Static *hWaveO.WAVEHDR
  
  Select uMsg
    Case #MM_WOM_DONE
      If Volume > 0 
        *hWaveO.WAVEHDR = lParam
        MAKE_WAVE(*hWaveO\lpData)
        *hWaveO\dwBytesRecorded = BlockSize
        waveOutWrite_(hWaveOut,lParam, SizeOf(WAVEHDR))
      EndIf
  EndSelect
  
  ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure

Procedure MAKE_WAVE(*SBuf)
  
  Static.d Angle, K, La, Ra
  Static.i sample
  Static.l Vl, Vr
  
  K = (2*#PI) * Frequency / SampleClock
  
  sample = 1

  Repeat
    
    If Volume < 0.9 And Up = #True
      Volume = Volume + 0.00002
    Else
      Up = #False
    EndIf
    
    If Volume > 0 And Up = #False
      Volume = Volume - 0.00002
    EndIf
    
    Vl = Sin(La) * 32767 * Volume
    La + K
    If La > (2*#PI) : La - (2*#PI) : EndIf
    PokeW(*SBuf,Vl)
    *SBuf + BytesPerSample
    
    Vr = Sin(Ra) * 32767 * Volume
    Ra + K
    If Ra > (2*#PI) : Ra - (2*#PI) : EndIf
    PokeW(*SBuf,Vr)
    *SBuf + BytesPerSample
    
    sample + PlayFormat\nBlockAlign
    
  Until sample > BlockSize
  
EndProcedure

OpenWindow(#Window,0,0,300,200,"Generator", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

SetWindowCallback(@WinCallback(), #Window)

With PlayFormat
  \wFormatTag = #WAVE_FORMAT_PCM
  \nChannels = Channels
  \wBitsPerSample = BytesPerSample * 8
  \nSamplesPerSec = SampleClock
  \nBlockAlign = Channels * BytesPerSample
  \nAvgBytesPerSec = \nSamplesPerSec * \nBlockAlign
EndWith

If *OutBufMem : FreeMemory(*OutBufMem) : EndIf
*OutBufMem = AllocateMemory(BlockSize * nBuf)

waveOutOpen_(@hWaveOut, #WAVE_MAPPER, @PlayFormat, WindowID(#Window), #True, #CALLBACK_WINDOW | #WAVE_FORMAT_DIRECT)

*P = *OutBufMem
For i = 0 To nBuf-1
  outHdr(i)\lpData = *P
  outHdr(i)\dwBufferLength = BlockSize
  outHdr(i)\dwFlags = 0
  outHdr(i)\dwLoops = 0
  waveOutPrepareHeader_(hWaveOut, outHdr(i), SizeOf(WAVEHDR))
  *P + BlockSize
Next

For i = 0 To nBuf-1
  PostMessage_(WindowID(#Window),#MM_WOM_DONE,0,outHdr(i))
Next 

Repeat
  Define Event = WaitWindowEvent() 
Until Event = #PB_Event_CloseWindow Or Volume <= 0 

waveOutReset_(hWaveOut)
For i = 0 To nBuf-1
  waveOutUnprepareHeader_(hWaveOut, outHdr(i), SizeOf(WAVEHDR))
Next
waveOutClose_(hWaveOut) 


Re: PlaySound with a 20KHz file produces sound ?!

Posted: Sat Jan 10, 2026 10:21 pm
by idle
Can you explain that a bit more, I'm not sure what you mean by wake up your speakers?

I was thinking more along the lines of air gapping like advertisers used to do or maybe still do by using in audible frequencies to tag what your watching on TV, so a listening device would id and tag and trace the user.

The code I posted above is working now though it DB level is very low not sure if that's just my recording settings though.

Re: PlaySound with a 20KHz file produces sound ?!

Posted: Sun Jan 11, 2026 4:43 am
by escape75
I have a friend who has a speaker that goes to sleep when there's no audio activity for a bit, and that setting cannot be disabled.

It actually takes a fairly long time to wake up as well which is annnoying, so I figured sending a short inaudible tone once a minute would fix that :)

Re: PlaySound with a 20KHz file produces sound ?!

Posted: Sun Jan 11, 2026 5:01 am
by idle
I'm guessing the speakers are wireles, I haven't encountered speakers going to sleep before but if it works great.

Re: PlaySound with a 20KHz file produces sound ?!

Posted: Sun Jan 11, 2026 6:10 am
by escape75
I think they are actually plugged in via analog input :)

I have a question about this line:

Code: Select all

((*fmtPtr\BitsPerSample + 7) / 8)
I've seen it also done this way, just wondering which way is correct:

Code: Select all

(*fmtPtr\BitsPerSample / 8)
I have added smooth volume increase in the code to make it not pop, as well as added so it plays for a duration of 1 second every 60 seconds, and then every 5 seconds when the computer wakes up from sleep 6 times in a row ... the reason for this is I haven't been able to figure out when the sound system gets ready after wake up ... as the program starts playing back on the lock screen and no sound is actually played!

Also an interesting issue I found is that when you do a mono sound, you can actually hear a faint sound:
CreateSound(#Sound,1000,16,1,96000,20000)

*Edit* I modifed the code to fix the mono issue (I think)


Here's my updated code:

Code: Select all


EnableExplicit

Enumeration 100
  #Window
  #Timer
  #Sound
EndEnumeration

Global Counter = -1

Structure RIFFStructure
  ChunkID.a[4]
  ChunkSize.l
  Format.a[4]
EndStructure

Structure fmtStructure
  Subchunk1ID.a[4]
  Subchunk1Size.l
  AudioFormat.u
  NumChannels.u
  SampleRate.l
  ByteRate.l
  BlockAlign.u
  BitsPerSample.u
EndStructure

Structure dataStructure
  Subchunk2ID.a[4]
  Subchunk2Size.l
EndStructure

Procedure ErrorHandler()
  Define ErrorMessage$ = ErrorMessage() + "(" + ErrorLine() + ")"
  MessageRequester("Program Error", ErrorMessage$)
  End
EndProcedure

OnErrorCall(@ErrorHandler())

Procedure FixSysTrayIcon(hWnd, uMsg, wParam, lParam)
  If uMsg = #WM_POWERBROADCAST And wParam = #PBT_APMRESUMESUSPEND
    RemoveWindowTimer(#Window, #Timer)
    AddWindowTimer(#Window, #Timer, 5000)
    Counter = 5
  EndIf
  
  ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure

Procedure.i CreateSound(Sound, Duration, BitsPerSample, NumChannels, SamplingRate, Frequency)
  
  Protected.i Result, HeaderSize, DataSize, Samples, Sample, Time.d, Tone.u, NumChannel
  Protected *WAVBuffer, *RiffPtr.RIFFStructure, *fmtPtr.fmtStructure, *dataPtr.dataStructure, *audioPtr
  
  HeaderSize = SizeOf(RIFFStructure)
  HeaderSize + SizeOf(fmtStructure)
  HeaderSize + SizeOf(dataStructure)
  
  DataSize = (BitsPerSample/8) * SamplingRate * Duration * NumChannels / 1000
  Samples = (DataSize / 2 / NumChannels) - 1
  
  *WAVBuffer = AllocateMemory(HeaderSize + DataSize, #PB_Memory_NoClear)
  
  If *WAVBuffer
    
    *RiffPtr = *WAVBuffer
    PokeS(@*RiffPtr\ChunkID, "RIFF", 4, #PB_Ascii|#PB_String_NoZero)
    *RiffPtr\ChunkSize = HeaderSize + DataSize - 8
    PokeS(@*RiffPtr\Format, "WAVE", 4, #PB_Ascii|#PB_String_NoZero)
    
    *fmtPtr = *WAVBuffer + SizeOf(RIFFStructure)
    PokeS(@*fmtPtr\Subchunk1ID, "fmt ", 4, #PB_Ascii|#PB_String_NoZero)
    *fmtPtr\Subchunk1Size = SizeOf(fmtStructure) - 8
    *fmtPtr\AudioFormat = 1
    *fmtPtr\NumChannels = NumChannels
    *fmtPtr\SampleRate = SamplingRate
    *fmtPtr\BlockAlign = *fmtPtr\NumChannels * (*fmtPtr\BitsPerSample/8)
    *fmtPtr\ByteRate = *fmtPtr\SampleRate * *fmtPtr\BlockAlign
    *fmtPtr\BitsPerSample = BitsPerSample
    
    *dataPtr = *WAVBuffer + SizeOf(RIFFStructure) + SizeOf(fmtStructure)
    PokeS(@*dataPtr\Subchunk2ID, "data", 4, #PB_Ascii|#PB_String_NoZero)
    *dataPtr\Subchunk2Size = DataSize
    
    *audioPtr = *WAVBuffer + SizeOf(RIFFStructure) + SizeOf(fmtStructure) + SizeOf(dataStructure)
    
    Define Steps.d = 0.9 / (Samples*0.25)
    Define Volume.d = 0.0
    
    For Sample = 0 To Samples
      
      If Sample < (Samples*0.25)
        Volume + Steps
      EndIf
      If Sample > (Samples*0.75)
        Volume - Steps
      EndIf
      
      Time = Sample / SamplingRate * 8
      Tone = Sin(Time * Frequency) * 32767 * Volume
      
      For NumChannel = 1 To NumChannels
        PokeU(*audioPtr, Tone)
        *audioPtr + 2
      Next
      
    Next
    
    Result = CatchSound(Sound, *WAVBuffer)
    FreeMemory(*WAVBuffer)
    
  EndIf
  
  ProcedureReturn Result
  
EndProcedure

OpenWindow(#Window, 0, 0, 300, 200, "20000Hz Sound", #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)

If InitSound()
  CreateSound(#Sound, 1000, 16, 2, 96000, 20000)
  PlaySound(#Sound) 
  AddWindowTimer(#Window, #Timer, 60000)
  SetWindowCallback(@FixSysTrayIcon(), #Window)
EndIf

Repeat
  
  Define Event = WaitWindowEvent(250)
  
  If Event = #PB_Event_Timer And EventTimer() = #Timer
    PlaySound(#Sound)
    If Counter = 0
      RemoveWindowTimer(#Window, #Timer)
      AddWindowTimer(#Window, #Timer, 60000)
    EndIf
    If Counter > -1
      Counter = Counter - 1
    EndIf
  EndIf
  
Until Event = #PB_Event_CloseWindow