Page 2 of 2
Re: PlaySound with a 20KHz file produces sound ?!
Posted: Sun Jan 11, 2026 7:50 am
by idle
I will look at it tomorrow. The channels are only valid for 2 it was a quick cut n paste and adaption. if you set it to one channel it would half the frequency effectively slowing it down if you set it to 4 it'd sound like a chipmunk.
Re: PlaySound with a 20KHz file produces sound ?!
Posted: Sun Jan 11, 2026 7:17 pm
by escape75
Sounds good, the only 2 main changes were:
Code: Select all
Samples = (DataSize / 2 / NumChannels) - 1 ; OLD: Samples = (DataSize / 2 / 2) - 1
For NumChannel = 1 To NumChannels ; OLD: *audioPtr\u= tone
PokeW(*audioPtr, Tone) ; OLD: *audioPtr+2
*audioPtr + 2 ; OLD: *audioPtr\u= tone
Next ; OLD: *audioPtr+2
Not sure if that's correct but seems to work ...
Re: PlaySound with a 20KHz file produces sound ?!
Posted: Sun Jan 11, 2026 9:34 pm
by idle
you'll know it's right by the sound, as in no clicks and right pitch. I'm not sure what I was doing with the alignment.
also should be
Code: Select all
For NumChannel = 1 To NumChannels
PokeW(*audioPtr, Tone)
*audioPtr + 2
Next
Re: PlaySound with a 20KHz file produces sound ?!
Posted: Sun Jan 11, 2026 9:43 pm
by escape75
Sometimes it helps for someone else to glance over the code to spot a mistake
I found out some clicks are caused by volume starting at 100% even though the sound should not be heard there's a click when beginning and ending playback ...
EDIT:
The For Next loop you specified was the same as mine, no?
Did you mean I shouldn't have changed:
Samples = (DataSize / 2 / NumChannels) - 1 ; OLD: Samples = (DataSize / 2 / 2) - 1
Re: PlaySound with a 20KHz file produces sound ?!
Posted: Sun Jan 11, 2026 9:54 pm
by idle
escape75 wrote: Sun Jan 11, 2026 9:43 pm
Sometimes it helps for someone else to glance over the code to spot a mistake
I found out some clicks are caused by volume starting at 100% even though the sound should not be heard there's a click when beginning and ending playback ...
I can't hear any clicks with your code.
And yes it helps to have someone glance at code, I'm a dyslexic, I don't see mistakes in my own code.
Re: PlaySound with a 20KHz file produces sound ?!
Posted: Sun Jan 11, 2026 10:02 pm
by escape75
I think it's safe to say the code works

Re: PlaySound with a 20KHz file produces sound ?!
Posted: Mon Jan 12, 2026 12:29 am
by escape75
I found an issue after analyzing the wave file and it seems the center frequency is 25460hz instead of 20000hz
I suspect the bug is around here somewhere:
Code: Select all
Time = Sample / SamplingRate * 8
Tone = Sin(Time * Frequency) * 32767 * Volume
The complete code with wave output, the program should be terminated right away as it generates the wave file that's needed quickly:
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.c
NumChannels.c
SampleRate.l
ByteRate.l
BlockAlign.c
BitsPerSample.c
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, 6000)
Counter = 5
EndIf
ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure
Procedure CreateSound(Sound, Duration, BitsPerSample, NumChannels, SamplingRate, Frequency)
Define HeaderSize, DataSize, Samples, Sample, Time.d, Tone.w, NumChannel
Define *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
PokeW(*audioPtr, Tone)
*audioPtr + 2
Next
Next
If CreateFile(0, GetHomeDirectory()+"\Desktop\Test.wav")
WriteData(0, *WAVBuffer, HeaderSize + DataSize)
CloseFile(0)
EndIf
CatchSound(Sound, *WAVBuffer)
FreeMemory(*WAVBuffer)
EndIf
EndProcedure
OpenWindow(#Window, 0, 0, 300, 70, "Wake Up", #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
TextGadget(123, 10, 25, 280, 40, "20000Hz Wake Up Test", #PB_Text_Center)
If InitSound()
CreateSound(#Sound, 1000, 16, 1, 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
When the sampling is set to 44100hz the center frequency changes to 18634hz
Re: PlaySound with a 20KHz file produces sound ?!
Posted: Mon Jan 12, 2026 12:43 am
by escape75
I have made the following changes, maybe someone can double check if this was a correct fix:
From
Code: Select all
Time = Sample / SamplingRate * 8
Tone = Sin(Time * Frequency) * 32767 * Volume
To
Code: Select all
Tone = Sin(2 * #PI * Frequency * Sample / SamplingRate) * 32767 * Volume
Re: PlaySound with a 20KHz file produces sound ?!
Posted: Mon Jan 12, 2026 2:52 am
by idle
yes that's what it should be.
Code: Select all
time = sample / SamplingRate
tone = Sin(time * 2 * #PI * frequency) * 32767
Re: PlaySound with a 20KHz file produces sound ?!
Posted: Mon Jan 12, 2026 5:45 am
by escape75
Ok perfect, that's what I have ...
Unfortnately I located another bug, with 24 bit generation, the 2 channels have different data, and playback is scrambled ... hmm!
Re: PlaySound with a 20KHz file produces sound ?!
Posted: Tue Jan 13, 2026 1:10 am
by idle
escape75 wrote: Mon Jan 12, 2026 5:45 am
Ok perfect, that's what I have ...
Unfortnately I located another bug, with 24 bit generation, the 2 channels have different data, and playback is scrambled ... hmm!
It needs modifying to do 24 and 32 bit so yes I will rewrite the procedure to handle variable bit sizes
Code: Select all
InitSound()
EnableExplicit
Structure WavHeader
chunkID.a[4] ; "RIFF"
chunkSize.l ; File size - 8
format.a[4] ; "WAVE"
subchunk1ID.a[4] ; "fmt "
subchunk1Size.l ; 16 for PCM
audioFormat.u ; 1 = PCM
numChannels.u
sampleRate.l
byteRate.l
blockAlign.u
bitsPerSample.u
subchunk2ID.a[4] ; "data"
subchunk2Size.l ; NumSamples * NumChannels * BitsPerSample/8
EndStructure
Procedure.i CreateSound(Sound.i, Duration.i, Bits.i=16, Channels.i=2, SamplingRate.i=44100, frequency=500)
Protected numSamples = SamplingRate * Duration / 1000
Protected bytesPerSample = Bits / 8
Protected dataSize = numSamples * Channels * bytesPerSample
Protected *header.WAVHeader, *data
Protected *WAVBuffer = AllocateMemory(dataSize + SizeOf(WavHeader))
If Not *WAVBuffer
ProcedureReturn #False
EndIf
*header = *WAVBuffer
PokeS(@*header\chunkID[0], "RIFF", 4, #PB_Ascii | #PB_String_NoZero)
*header\chunkSize = 36 + dataSize
PokeS(@*header\format[0], "WAVE", 4, #PB_Ascii | #PB_String_NoZero)
PokeS(@*header\subchunk1ID, "fmt ", 4, #PB_Ascii | #PB_String_NoZero)
*header\subchunk1Size = 16
*header\audioFormat = 1
*header\numChannels = Channels
*header\sampleRate = SamplingRate
*header\byteRate = SamplingRate * Channels * bytesPerSample
*header\blockAlign = Channels * bytesPerSample
*header\bitsPerSample = Bits
PokeS(@*header\subchunk2ID, "data", 4, #PB_Ascii | #PB_String_NoZero)
*header\subchunk2Size = dataSize
*data = *WAVBuffer + SizeOf(WavHeader)
Protected a,i,maxAmplitude
Protected time.d,sample,result
Select Bits
Case 16
maxAmplitude = 32767
Case 24
maxAmplitude = 8388607
Case 32
maxAmplitude = 2147483647
Default
maxAmplitude = 32767
EndSelect
numSamples - 1
For i = 0 To numSamples
time = i / SamplingRate
sample = maxAmplitude * Sin(2.0 * #PI * frequency * time)
For a = 1 To Channels
Select bytesPerSample
Case 2
PokeW(*data, sample)
*data + 2
Case 3
PokeB(*data, sample & $FF)
PokeB(*data + 1, (sample >> 8) & $FF)
PokeB(*data + 2, (sample >> 16) & $FF)
*data + 3
Case 4
PokeL(*data, sample)
*data + 4
EndSelect
Next
Next
Result = CatchSound(Sound, *WAVBuffer)
FreeMemory(*WAVBuffer)
ProcedureReturn Result
EndProcedure
CreateSound(0, 1000, 24, 2, 96000, 1000)
PlaySound(0)
Delay(1000)
Re: PlaySound with a 20KHz file produces sound ?!
Posted: Tue Jan 13, 2026 6:13 pm
by escape75
Great, I was fixing it in a similar way but yours is a bit cleaner
Couple questions ...
Would memory de-allocate here cause any issues:
Code: Select all
CatchSound(Sound, *WAVBuffer)
FreeMemory(*WAVBuffer)
I was thinking make it a global at the start of the program and don't free until program exit, although PB might automatically free it anyways ...
Re: PlaySound with a 20KHz file produces sound ?!
Posted: Tue Jan 13, 2026 7:29 pm
by idle
I'm not very good at explaining. Pb doesn't garbage collect memory you allocatememory with so it's better to minimize the memory lifetime and scope and in this case it's lifetime is done once it's copied to the sound object. So you free it.
Re: PlaySound with a 20KHz file produces sound ?!
Posted: Wed Jan 14, 2026 9:13 pm
by escape75
Ok that makes sense, although I've noticed that when you FreeMemory it just overwrites a few bytes at the beginning of the buffer
and you can actually make a second call to CatchSound afterwards and it will still play correctly as just the text strings are corrupted ...
This makes it hard to test whether the buffer is actually copied or just used as is
In any case here's the 2 examples I have, both work the same, but done in 2 different ways:
Code: Select all
EnableExplicit
Enumeration 100
#Window
#Sound
EndEnumeration
Global *WAVBuffer
Structure WAVFormat
ChunkID.a[4]
ChunkSize.l
Format.a[4]
Subchunk1ID.a[4]
Subchunk1Size.l
AudioFormat.w
NumChannels.w
SampleRate.l
ByteRate.l
BlockAlign.w
BitsPerSample.w
Subchunk2ID.a[4]
Subchunk2Size.l
EndStructure
Procedure CreateSound(Sound, Duration, Frequency)
Protected NumChannels = 1
Protected BitsPerSample = 16
Protected SamplingRate = 44100
Protected Samples, DataSize, Sample
Protected *fmtPtr.WAVFormat, *datPtr
Samples = SamplingRate * Duration
DataSize = Samples * NumChannels * (BitsPerSample/8)
*WAVBuffer = AllocateMemory(SizeOf(WAVFormat) + DataSize)
If *WAVBuffer
*fmtPtr = *WAVBuffer
PokeS(@*fmtPtr\ChunkID, "RIFF", 4, #PB_Ascii | #PB_String_NoZero)
*fmtPtr\ChunkSize = 36 + DataSize
PokeS(@*fmtPtr\Format, "WAVE", 4, #PB_Ascii | #PB_String_NoZero)
PokeS(@*fmtPtr\Subchunk1ID, "fmt ", 4, #PB_Ascii | #PB_String_NoZero)
*fmtPtr\Subchunk1Size = 16
*fmtPtr\AudioFormat = 1
*fmtPtr\NumChannels = NumChannels
*fmtPtr\SampleRate = SamplingRate
*fmtPtr\ByteRate = SamplingRate * NumChannels * (BitsPerSample/8)
*fmtPtr\BlockAlign = NumChannels * (BitsPerSample/8)
*fmtPtr\BitsPerSample = BitsPerSample
PokeS(@*fmtPtr\Subchunk2ID, "data", 4, #PB_Ascii | #PB_String_NoZero)
*fmtPtr\Subchunk2Size = DataSize
*datPtr = *WAVBuffer + SizeOf(WAVFormat)
For Sample = 1 To Samples
PokeW(*datPtr, Sin(2 * #PI * Frequency * Sample / SamplingRate) * 32767)
*datPtr + 2
Next
CatchSound(Sound, *WAVBuffer)
EndIf
EndProcedure
OpenWindow(#Window, 0, 0, 400, 200, "Test", #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
If InitSound()
CreateSound(#Sound, 1, 440)
PlaySound(#Sound, #PB_Sound_Loop)
EndIf
Repeat
Define Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow
StopSound(#Sound)
If *WAVBuffer : FreeMemory(*WAVBuffer) : EndIf
Code: Select all
EnableExplicit
Enumeration 100
#Window
#WebView
EndEnumeration
OpenWindow(#Window, 100, 100, 400, 200, "Test", #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
WebViewGadget(#WebView, 0, 0, 400, 200)
SetGadgetItemText(#WebView, #PB_WebView_HtmlCode, ~"<script>ctx = new AudioContext(); osc = ctx.createOscillator(); osc.type = 'sine'; osc.frequency.value = 440; osc.connect(ctx.destination);</script>")
WebViewExecuteScript(#WebView, ~"osc.start(0);")
Repeat
Define Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow
Re: PlaySound with a 20KHz file produces sound ?!
Posted: Wed Jan 14, 2026 9:37 pm
by idle
escape75 wrote: Wed Jan 14, 2026 9:13 pm
Ok that makes sense, although I've noticed that when you FreeMemory it just overwrites a few bytes at the beginning of the buffer
that's normal, the memory doesn't get zeroed it just get returned to the pool