Skip to content

Instantly share code, notes, and snippets.

@aziascreations
Last active May 16, 2019 01:34
Show Gist options
  • Save aziascreations/8b7728d90f98a66828ca74b6853aaf32 to your computer and use it in GitHub Desktop.
Save aziascreations/8b7728d90f98a66828ca74b6853aaf32 to your computer and use it in GitHub Desktop.
Generate a sine wave at a fixed frequency and save it in a .wav file
;EnableExplicit
; Original ASM code for .l
; https://www.purebasic.fr/english/viewtopic.php?f=19&t=17427
Macro EndianSwap(Number)
CompilerSelect TypeOf(Number)
CompilerCase #PB_Word
EndianSwapW(Number)
CompilerCase #PB_Long
EndianSwapL(Number)
CompilerDefault
CompilerError "Unsupported value type given in '+EndianSwap(Number)' !"
CompilerEndSelect
EndMacro
Procedure.l EndianSwapW(Number.l)
EnableASM
MOV ax,Number
XCHG al,ah
MOV Number,ax
DisableASM
ProcedureReturn Number
EndProcedure
; FIXME: Should x64 use 'rax' or does it supports 'eax' too ?
Procedure.l EndianSwapL(Number.l)
EnableASM
MOV eax,Number
BSWAP eax
MOV Number,eax
DisableASM
ProcedureReturn Number
EndProcedure
CompilerIf #PB_Compiler_IsMainFile
Debug "Word:"
Debug "0d42" + #TAB$ +
"0x"+RSet(Hex(42, #PB_Long), 2*2, "0") + #TAB$ + " ->" + #TAB$ +
"0x"+RSet(Hex(EndianSwapL(42), #PB_Long), 2*2, "0")
Debug "0d420" + #TAB$ +
"0x"+RSet(Hex(420, #PB_Long), 2*2, "0") + #TAB$ + " ->" + #TAB$ +
"0x"+RSet(Hex(EndianSwapL(420), #PB_Long), 2*2, "0")
Debug "Long:"
Debug "0d42" + #TAB$ +
"0x"+RSet(Hex(42, #PB_Long), 4*2, "0") + #TAB$ + " ->" + #TAB$ +
"0x"+RSet(Hex(EndianSwapL(42), #PB_Long), 4*2, "0")
Debug "0d420" + #TAB$ +
"0x"+RSet(Hex(420, #PB_Long), 4*2, "0") + #TAB$ + " ->" + #TAB$ +
"0x"+RSet(Hex(EndianSwapL(420), #PB_Long), 4*2, "0")
CompilerEndIf
; IDE Options = PureBasic 5.62 (Windows - x64)
; CursorPosition = 16
; FirstLine = 2
; Folding = -
; EnableXP
; File format documentation
; http://tiny.systems/software/soundProgrammer/WavFormatDocs.pdf
XIncludeFile "./EndianUtils.pbi"
; ------
Structure RIFFChunk
ChunkID.l ;BE
ChunkSize.l ;LE
Format.l ;BE
EndStructure
Structure fmtChunk
Subchunk1ID.l ;BE
Subchunk1Size.l ;LE (For the rest of the sub-chunk)
AudioFormat.w
NumChannels.w
SampleRate.l
ByteRate.l
BlockAlign.w
BitsPerSample.w
EndStructure
Structure dataChunk
Subchunk2ID.l ;BE
Subchunk2Size.l ;LE (For the rest of the sub-chunk)
*Data
EndStructure
Structure WAVFormat
RIFF.RIFFChunk
fmt.fmtChunk
dat.dataChunk
EndStructure
; ------
Procedure CreateWaveStructure(AudioFormat.w, NumChannels.w, SampleRate.l, BitsPerSample.w, NumSamples.l)
Protected *Wav.WAVFormat = AllocateMemory(SizeOf(WAVFormat))
If *Wav
*Wav\RIFF\ChunkID = EndianSwapL($52494646) ;"RIFF"
*Wav\RIFF\ChunkSize = 0
*Wav\RIFF\Format = EndianSwapL($57415645) ;"WAVE"
*Wav\fmt\Subchunk1ID = EndianSwapL($666d7420) ;"fmt "
*Wav\fmt\Subchunk1Size = 16 ; See linked PDF for more info about this one.
*Wav\fmt\AudioFormat = AudioFormat
*Wav\fmt\NumChannels = NumChannels
*Wav\fmt\SampleRate = SampleRate
*Wav\fmt\ByteRate = SampleRate * NumChannels * (BitsPerSample / 8)
*Wav\fmt\BlockAlign = NumChannels * (BitsPerSample / 8)
*Wav\fmt\BitsPerSample = BitsPerSample
*Wav\dat\Subchunk2ID = EndianSwapL($64617461)
*Wav\dat\Subchunk2Size = NumSamples * NumChannels * (BitsPerSample / 8)
*Wav\dat\Data = AllocateMemory(*Wav\dat\Subchunk2Size)
If Not *Wav\dat\Data
FreeMemory(*Wav)
*Wav = #Null
Else
*Wav\RIFF\ChunkSize = 36 + *Wav\dat\Subchunk2Size
EndIf
EndIf
ProcedureReturn*Wav
EndProcedure
CompilerIf #PB_Compiler_IsMainFile
; Uncompressed, Mono, 384000bps, 8bps, 3sec
*Wav.WAVFormat = CreateWaveStructure(1, 1, 48000, 8, 48000 * 3)
If Not *Wav
Debug "Wave Structure creation failure !"
End 1
EndIf
;Debug "Wave data buffer size: "+MemorySize(*Wav\dat\Data)
Define Freq.d = 420 ; in Hz
Define i.l, result.b
Define s.d = *Wav\fmt\SampleRate / Freq ; nb of samples per cycle
Define a.d ; a=(i mod s) => nth step in the current cycle == [0;s-1]
Define b.d ; b=a/s => "progression" within the current cycle == [0;1[
; Formula = f(x) = sin(rad(b*360)) -> where sin(...) returns [0;255] or [0;bps*8-1]
For i=0 To MemorySize(*Wav\dat\Data)
a = Mod(i, s)
b = a/s
; The second '127' is not 128 so I don't have to deal with potential overflows.
value = 127 + (127 * Sin(Radian(b*360)))
PokeA(*Wav\dat\Data + i, value)
Next
If CreateFile(0, "./out-"+Str(Date())+"-"+Str(Freq)+"Hz.wav")
; TODO: Write it in 2 calls.
WriteData(0, *Wav, SizeOf(RIFFChunk))
WriteData(0, *Wav + SizeOf(RIFFChunk), SizeOf(fmtChunk))
WriteData(0, *Wav + SizeOf(RIFFChunk) + SizeOf(fmtChunk), 2*4)
WriteData(0, *Wav\dat\Data, MemorySize(*Wav\dat\Data))
CloseFile(0)
Else
Debug "File creations failure !"
EndIf
FreeMemory(*Wav\dat\Data)
FreeMemory(*Wav)
CompilerEndIf
; IDE Options = PureBasic 5.62 (Windows - x64)
; CursorPosition = 104
; FirstLine = 84
; Folding = -
; EnableXP
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment