Last active
May 16, 2019 01:34
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; 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