Beruflich Dokumente
Kultur Dokumente
Theblogatthebottomofthesea
ProgrammingandGamedevStuff
DIYSynth2:CommonWaveForms
PostedonMay192012byDemofox
ThisisapartoftheDIYSynthesizerseriesofpostswhereeachpostisroughlybuiltupontheknowledgeofthe
previousposts.Ifyouarelost,checktheearlierposts!
Thisisthesecondchapterinaseriesoftutorialsaboutprogrammingyourownsynthesizer
Inthischapterwelltalkaboutoscillators,andsomecommonbasicwaveforms:Sine,Square,Saw,Triangleand
Noise.
Bytheend,youshouldhaveenoughknowledgetomakesomebasicelectronicmelodies.
Youcandownloadthefullsourceforthischapterhere:DIYSynthesizer:Chapter2SourceCode
TheSineWave
Thesinewaveisthebasisoflotsofthingsinaudiosynthesis.Itcanbeusedonitsowntomakesound,multiple
sinewavescanbecombinedtomakeothermorecomplexwaveforms(aswellseeinthenextchapter)anditsalso
thebasisofalotofDSPtheoryandaudioanalysis.Forinstance,thereissomethingcalledFourierAnalysiswhere
youcananalyzesomeaudiodataanditwilltellyouwhataudiofrequenciesareinthatsounddata,andhowstrong
eachis(usefulforadvancedsynthesisanddigitalsignalprocessingakaDSP).Themathofhowtogetthat
informationisbasedonsomesimplepropertiesofsinewaves.Moreinfocanbefoundhere:
http://en.wikipedia.org/wiki/Fourier_analysis.
Ifwewanttouseasinewaveinouraudiodata,thefirstproblemwehitisthatsinehasavaluefrom1to1,butour
audiodatafromthelastchapterisstoredina32bitint,whichhasarangeof2,147,483,648to2,147,483,647,and
isunabletostorefractionalnumbers.
Thesolutionistojustmap1to2,147,483,648,and1to2,147,483,647andallthenumbersinbetweenrepresent
fractionalnumbersbetween1and1.0.25forinstancewouldbecome536,870,911.
Ifinsteadof32bits,wewantedtostorethedatain16bits,or8bits,wecoulddothataswell.Aftergeneratingour
floatingpointaudiodata,wejustconvertitdifferentlytogettothose16bitsand8bits.16bitshavearangeof
http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 1/11
11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea
32,768to32,767so0.25wouldconvertto8191.In8bits,wavefileswantUNSIGNED8bitnumbers,sotherange
is0to255.Inthatcase,0.25wouldbecome158.
Note,inthecodeforthischapter,imodifiedWriteWaveFiletodothisconversionforussogoingforwardwecan
workwithfloatingpointnumbersonlyandnotworryaboutbitspersampleuntilwewanttowritethewavefile.When
youcallthefunction,youhavetogiveitatemplateparameterspecifyingwhatTYPEyouwanttouseforyour
samples.Thethreesupportedtypesareuint8,int16andint32.Forsimplewaveformslikethoseweareworkingwith
today,thereisnoaudibledifferencebetweenthe3,soallthesamplesjustmake16bitwavefiles.
So,webustoutsomemathandfigureouthereshowtogenerateasinewave,respectingthesamplerateand
frequencywewanttouse:
//makeanaivesinewave
for(intnIndex=0;nIndex<nNumSamples;++nIndex)
{
pData[nIndex]=sin((float)nIndex*2*(float)M_PI*fFrequency/(float)nSampleRate);
}
WriteWaveFile<int16>("sinenaive.wav",pData,nNumSamples,nNumChannels,nSampleRate);
Thatdoeswork,andifyoulistentothewavefile,itdoessoundcorrect:
NaiveSineWaveGeneration
Itevenlookscorrect:
Thereisasubtleproblemwhengeneratingthesinewavethatwaythoughwhichwewilltalkaboutnext.
PoppingakaDiscontinuity
Theproblemwithhowwegeneratedthewavefileonlybecomesapparentwhenwetrytoplaytwotonesrightnextto
eachother,likeinthefollowingcodesegment:
//makeadiscontinuitous(popping)sinewave
for(intnIndex=0;nIndex<nNumSamples;++nIndex)
{
if(nIndex<nNumSamples/2)
{
floatfCurrentFrequency=CalcFrequency(3,3);
pData[nIndex]=sin((float)nIndex*2*(float)M_PI*fCurrentFrequency/
(float)nSampleRate);
}
else
{
floatfCurrentFrequency=CalcFrequency(3,4);
pData[nIndex]=sin((float)nIndex*2*(float)M_PI*fCurrentFrequency/
(float)nSampleRate);
}
http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 2/11
11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea
}
WriteWaveFile<int16>("sinediscon.wav",pData,nNumSamples,nNumChannels,nSampleRate);
Quicknoteaboutanewfunctionshownhere,calledCalcFrequency.Imadethatfunctionsothatyoupassthenote
youwant,andtheoctaveyouwant,anditwillreturnthefrequencyforthatnote.Forinstance,togetmiddleCaka
C4(thetoneallthesesamplesuse),youuseCalcFrequency(3,3),whichreturnsapproximately261.626.
Listentothewavefilegeneratedandyoucanhearapoppingnoisewherethetonechangesfromonefrequencyto
thenext:DiscontinuousSineWave
Sowhyisthis?Thereasonisbecausehowwearegeneratingoursinewavesmakesadiscontinuitywherethe2
wavefileschange.
Hereyoucanseethepointthatthefrequencieschangeandhowaprettysmalldiscontinuitycanmakeaprettybig
impactonyoursound!Thesoundyouarehearinghasanofficialname,calledapop(DSP/synth/otheraudio
peoplewilltalkaboutpoppingintheiraudio,anddiscontinuityisthereasonforit)
Sohowdowefixit?Insteadofmakingthesinewaveberigidlybasedontime,whereforeachpoint,wecalculate
thesinevaluewithnoregardtopreviousvalues,weuseaFreeSpinningOscillator.
ThatisafancywayofsayingwejusthaveavariablekeeptrackofthecurrentPHASE(angle)thatweareatinthe
sinewaveforthecurrentsample,andtogetthenextsample,weadvanceourphasebasedonthefrequencyatthe
time.Basicallyouroscillatorisawheelthatspinsfreely,andourcurrentfrequencyjustsayshowfasttoturnthe
wheel(fromwhereveritisnow)togetthevalueforthenextsample.
Hereswhatthelookslikeincode:
//makeacontinuoussinewavethatchangesfrequencies
for(intnIndex=0;nIndex<nNumSamples;++nIndex)
{
if(nIndex<nNumSamples/2)
{
floatfCurrentFrequency=CalcFrequency(3,3);
fPhase+=2*(float)M_PI*fCurrentFrequency/(float)nSampleRate;
while(fPhase>=2*(float)M_PI)
fPhase=2*(float)M_PI;
while(fPhase<0)
fPhase+=2*(float)M_PI;
pData[nIndex]=sin(fPhase);
}
else
http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 3/11
11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea
{
floatfCurrentFrequency=CalcFrequency(3,4);
fPhase+=2*(float)M_PI*fCurrentFrequency/(float)nSampleRate;
while(fPhase>=2*(float)M_PI)
fPhase=2*(float)M_PI;
while(fPhase<0)
fPhase+=2*(float)M_PI;
pData[nIndex]=sin(fPhase);
}
}
WriteWaveFile<int16>("sinecon.wav",pData,nNumSamples,nNumChannels,nSampleRate);
Notethatwekeepthephasebetween0and2*PI.Theresnomathematicalreasonforneedingtodothis,butin
floatingpointmath,ifyouletavaluegettoolarge,itstartstoloseprecision.Thatmeans,thatifyoumadeawave
filethatlastedalongtime,theaudiowouldstarttodegradethelongeritplayed.Ialsouseawhileloopinsteadofa
regularifstatement,becauseifsomeoneusesverylargefrequencies,youcanpass2*PIacoupleoftimesina
singlesample.Also,icheckthatitsabovezero,becauseitisvalidtousenegativefrequencyvalues!Allstufftobe
mindfulofwhenmakingyourownsynthprograms(:
Hereswhatthegeneratedwavefilesoundslike,noticethesmoothtransitionbetweenthetwonotes:
ContinuousSineWave
Andhereswhatitlookslikevisuallywherethewavechangesfrequency,whichyoucanseeisniceandsmooth(the
bottomwave).Thetopwaveisthepoppingsinewaveimageagainatthesamepointintimeforreference.Onthe
smoothwaveitisntevenvisuallynoticeablethatthefrequencyhaschanged.
Onelastwordonthispoppingisactuallysometimesdesiredandcanhelpmakeupapartofagoodsound.For
instance,somepercussionsoundscanmakeuseofpoppingtosoundmoreappropriate!
SineWaveOscillator
Forourfinalincarnationofasinewaveoscillator,heresanicesimplehelperfunction:
floatAdvanceOscilator_Sine(float&fPhase,floatfFrequency,floatfSampleRate)
{
fPhase+=2*(float)M_PI*fFrequency/fSampleRate;
http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 4/11
11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea
while(fPhase>=2*(float)M_PI)
fPhase=2*(float)M_PI;
while(fPhase<0)
fPhase+=2*(float)M_PI;
returnsin(fPhase);
}
Youpassthatfunctionyourcurrentphase,thefrequencyyouwant,andthesamplerate,anditwilladvanceyour
phase,andreturnthevalueforyournextaudiosample.
Heresanexampleofhowtouseit:
//makeasinewave
for(intnIndex=0;nIndex<nNumSamples;++nIndex)
{
pData[nIndex]=AdvanceOscilator_Sine(fPhase,fFrequency,(float)nSampleRate);
}
WriteWaveFile<int16>("sine.wav",pData,nNumSamples,nNumChannels,nSampleRate);
Hereswhatitsoundslike(nothingnewatthispoint!):
VanillaSineWave
WaveAmplitude,VolumeandClipping
YoucanadjusttheAMPLITUDEofanywaveformbymultiplyingeachsamplebyavalue.Valuesgreaterthanone
increasetheamplitude,makingitlouder,valueslessthanonedecreasetheamplitude,makingitquieter,and
negativevaluesflipthewaveover,butalsohavetheabilitytomakeitquieterorlouder.
Oneplacepeopleusenegativeamplitudes(volumes)isfornoisecancellation.Ifyouhaveacomplexsoundthathas
somenoiseinit,butyouknowthesourceofthenoise,youcantakethatnoice,multiplyitby1togetavolumeof
1,andADDIT(orMIXIT)intothemorecomplexsound,effectivelyremovingthenoisefromthesound.Thereare
otherusestoobutthisisoneconcrete,realworldexample.
Thiscodesamplegeneratesaquieterwavefile:
//makeaquietersinewave
for(intnIndex=0;nIndex<nNumSamples;++nIndex)
{
pData[nIndex]=AdvanceOscilator_Sine(fPhase,fFrequency,(float)nSampleRate)*0.4f;
}
WriteWaveFile<int16>("sinequiet.wav",pData,nNumSamples,nNumChannels,nSampleRate);
Andhereswhatthatsoundslike:
VanillaSineWaveQuiet
http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 5/11
11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea
Andhereswhatthatlookslike:
Ifyourecallthough,whenwewriteawavefile,wemap1tothesmallestintnumberwecanstore,and1tothe
highestintnumberwecanstore.Whathappensifwemakesomethingtooloud,sothatitgoesabove1.0orbelow
1.0?
OnewaytofixthiswouldbetoNormalizethesounddata.Tonormalizeit,youwouldloopthrougheachsamplein
thestreamandfindthehighestabsolutevaluesample.Forinstanceifyouhad3samples:1.0,1.2,0.8,the
highestabsolutesamplevaluewouldbe1.2.
Onceyouhavethisvalue,youloopthroughthesamplesinthestreamanddividebythisnumber.Afteryoudothis,
everysampleinthestreamwillbewithintherange1to1.Notethatifyouhadanydatathatwouldbeclipping,this
processhasthesideeffectofmakingyourentirestreamquietersinceitreducestheamplitudeofeverysample.If
youdidnthaveanyclippingdata,thisprocesshasthesideeffectofmakingyourentirestreamlouderbecauseit
increasestheamplitudeofeverysample.
Anotherwaytodealwithitistojustclampthevaluestothe1,1range.Inthecaseofasinewave,thatmeanswe
chopoffthetopand/orthebottomofthewaveandtheresjustaflatplateauwherethenumberswentoutofrange.
Thisiscalledclipping,andalongwithpoppingare2ofthemainproblemspeoplehavewithaudioqualitydegradation.
Aliasingisathird,andissomethingweaddressinthenextchapterbytheway!
(http://en.wikipedia.org/wiki/Aliasing)
Heressomecodeforgeneratingaclippingsinewave:
//makeaclippingsinewave
for(intnIndex=0;nIndex<nNumSamples;++nIndex)
{
pData[nIndex]=AdvanceOscilator_Sine(fPhase,fFrequency,(float)nSampleRate)*1.4f;
}
WriteWaveFile<int16>("sineclip.wav",pData,nNumSamples,nNumChannels,nSampleRate);
Andhereswhatitsoundslike:
VanillaSineWaveClipping
Also,hereswhatitlookslike:
Notethatinthiscase,itdoesntnecessarilysoundBADcomparedtoaregular,nonclippingsinewave,butitdoes
sounddifferent.Thatmightbeagoodthing,orabadthing,dependingonyourintentions.Withmorecomplex
sounds,likevoice,oracousticmusic,thiswillusuallymakeitsoundterrible.Audioengineershavetocarefully
controlthelevels(volumes)ofthechannelsbeingmixed(added)togethertomakesuretheresultingoutputdoesnt
gooutsideofthevalidrangeandcauseclipping.Also,inanaloghardware,goingoutofrangecancausedamageto
thedevicesiftheyarentbuilttoprotectthemselvesfromit!
http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 6/11
11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea
Inthecaseofrealtimesynthesis,asyoumightimagine,normalizingwavedataisimpossibletodobecauseit
requiresthatyouknowallthesounddataupfronttobeabletonormalizethedata.Inrealtimeapplications,besides
justmakingsurethelevelskeepeverythinginrange,youalsohavetheoptionofusingacompressorwhichsortof
dynamicallynormalizesonthefly.Checkthisoutformoreinformation:
http://en.wikipedia.org/wiki/Dynamic_range_compression
SquareWaveOscillator
Heresthecodeforthesquarewaveoscillator:
floatAdvanceOscilator_Square(float&fPhase,floatfFrequency,floatfSampleRate)
{
fPhase+=fFrequency/fSampleRate;
while(fPhase>1.0f)
fPhase=1.0f;
while(fPhase<0.0f)
fPhase+=1.0f;
if(fPhase<=0.5f)
return1.0f;
else
return1.0f;
}
Notethatweareusingthephaseasifitsapercentage,insteadofanangle.Sinceweareusingitdifferently,that
meansifyouswitchfromsinewavetosquarewave,therewillbeadiscontinuity(apop).However,inpracticethis
happensanywaysalmostallthetimebecauseunlessyouchangefromsinetosquareattheverytoporbottomof
thesinewave,therewillbediscontinuityanyways.Inreality,thisreallydoesntmatter,butyoucouldfixitto
switchonlyonthoseboundaries,oryoucouldusecrossfadingorblendingtofadeonewaveout(decrease
amplitudefrom1to0),whilebringingthenewwavein(increaseamplitudefrom0to1),addingthemtogethertoget
theoutput.Doingsowillmakeasmoothtransitionbutaddssomecomplexity,andsquarewavesbynature
constantlypopanywaysitswhatgivesthemtheirsound!
Hereswhatasquarewavesoundslikeandlookslike:
SquareWave
SawWaveOscillator
Weusedthesawwaveinchapterone.Heresthecodeforasawwaveoscillator:
floatAdvanceOscilator_Saw(float&fPhase,floatfFrequency,floatfSampleRate)
{
fPhase+=fFrequency/fSampleRate;
http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 7/11
11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea
while(fPhase>1.0f)
fPhase=1.0f;
while(fPhase<0.0f)
fPhase+=1.0f;
return(fPhase*2.0f)1.0f;
}
Hereswhatasawwavelooksandsoundslike:
SawWave
Notethatsometimessawwavespointtheotherdirectionandthedropoffisontheleftinsteadofontheright,and
therestofthewaydescendsinsteadofrisesbutasfarasIhaveseen,thereisnoaudibleorpracticaldifference.
TriangleWaveOscillator
Alotofsynthsdontevenbotherwithatrianglewave,andthosethatdo,arejustforapproximationsofasinewave.
Atrianglewavesoundsalotlikeasinewaveandlooksabitlikeittoo.
Heresthecodeforatrianglewaveoscillator:
floatAdvanceOscilator_Triangle(float&fPhase,floatfFrequency,floatfSampleRate)
{
fPhase+=fFrequency/fSampleRate;
while(fPhase>1.0f)
fPhase=1.0f;
while(fPhase<0.0f)
fPhase+=1.0f;
floatfRet;
if(fPhase<=0.5f)
fRet=fPhase*2;
else
fRet=(1.0ffPhase)*2;
return(fRet*2.0f)1.0f;
}
Hereswhatitlooksandsoundslike:
TriangleWave
http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 8/11
11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea
NoiseOscillator
Believeitornot,evenstatichasitsplacetoo.Itsusedsometimesforpercussion(putanenvelopearoundsome
statictomakeaclapsound),itcanbeusedasalowfrequencyoscillatorakaLFO(theoldholdandsampletype
stuff)andotherthingsaswell.Staticisjustrandomaudiosamples.
Thecodeforanoiseoscillatorisslightlydifferentthantheothers.Youhavetopassitthelastsamplegenerated(you
canpass0ifitsthefirstsample)anditwillcontinuereturningthatlastvalueuntilitstimetogenerateanewrandom
number.Itdetermineswhenitstimebasedonthefrequencyyoupassin.Ahigherfrequencymeanmorerandom
numberswillbechoseninthesameamountofaudiodatawhilealowerfrequencymeansthatfewerrandom
numberswillbechosen.
Atlowerfrequencies(likeinthesample),itkindofsoundslikeanexplosionorrocketshipsoundeffectfromthe80s
whichisfun
Heresthecode:
floatAdvanceOscilator_Noise(float&fPhase,floatfFrequency,floatfSampleRate,floatfLastValue)
{
unsignedintnLastSeed=(unsignedint)fPhase;
fPhase+=fFrequency/fSampleRate;
unsignedintnSeed=(unsignedint)fPhase;
while(fPhase>2.0f)
fPhase=1.0f;
if(nSeed!=nLastSeed)
{
floatfValue=((float)rand())/((float)RAND_MAX);
fValue=(fValue*2.0f)1.0f;
//uncommentthebelowtomakeitslightlymoreintense
/*
if(fValue<0)
fValue=1.0f;
else
fValue=1.0f;
*/
returnfValue;
}
else
{
returnfLastValue;
http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 9/11
11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea
}
}
Hereswhatitlooksandsoundslike:
Noise
IthinkitkindoflooksliketheArizonadesert
Asaquickaside,ihavetherandomnumbersasrandomfloatingpointnumbers(theycanbeanythingbetween1.0
and1.0).AnotherwaytogeneratenoiseistomakeitsoitwillchooseonlyEITHER1or1andnothinginbetween.
Itgivesaslightlyharshersound.Thecodetodothatisintheoscillatorifyouwanttotryitout,itsjustcommented
out.Thereareotherwaystogeneratenoisetoo(checkoutpinknoisehttp://en.wikipedia.org/wiki/Pink_noise)
butthisoughttobegoodenoughforourimmediateneeds!
MoreExoticWaveForms
TwootheroscillatorsIveusedonoccasionisthesquaredsinewaveandtherectanglewave.
Tocreateasquaredsinewaveallyouneedtodoismultiplyeachsamplebyitself(squaretheaudiosample).This
makesawaveformthatissimilartosinewaves,butalittlebitdifferent,andsoundsabitdifferenttoo.
Arectanglewaveiscreatedbymakingitsothewavespendseithermoreorlesstimeintheupordownpartof
thewave.Insteadofitbeing50%ofthetimeinup,and50%ofthetimeindownyoucanmakeitsoitspends
80%ofthetimeinup,and20%ofthetimeindown.Itmakesitsoundquiteabitdifferent,andthemoredifferentthe
percentagesare,thebrighteritsounds.
Also,youcanaddmultiplewaveformsamplestogethertogetmoreinterestingwaveforms(likeaddingatriangle
andasquarewaveofthesamefrequencytogether,andreducingtheamplitudetoavoidclipping).Thatscalled
additivesynthesisandwelltalkmoreaboutthatnextchapter,includinghowtomakemorecorrectwaveformsusing
sinewavestoavoidaliasing.
Youcanalsomultiplywaveformstogethertocreateother,moreinterestingwaves.Strictlyspeakingthisiscalled
AMsynthesis(amplitudemodulationsynthesis)whichisalsosometimesknownasringmodulationwhendonea
certainway.
Asyoucansee,therearealotofdifferentwaystocreateoscillators,andthewaveformsarejustlimitedbyyour
imagination.Playaroundandtrytomakeyourownoscillatorsandexperiment!
FinalSamples
Nowwehavethesimplebasicsdownforbeingabletocreatemusic.heresasmallsongthatisgeneratedinthe
samplecode:
SimpleSong
http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 10/11
11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea
Andjusttoreinforcehowimportantkeepingyourwavedatacontinuousis,heresthesamewavefile,butabout0.75
secondsinaputaSINGLE1.0samplewhereitdoesntbelong.asinglesamplewrongwhentheres44100samples
persecondandlookhowmuchitaffectstheaudio.
SimpleSongWithPop
UntilNextTime
Nextupwewilltalkaboutaliasingandhowtoavoidit,makingmuchbettersoundingsaw,squareandtriangle
wavesthatarelessharshontheears.
Likethis:
Like
Onebloggerlikesthis.
Related
Comments
0comments
Addacomment...
FacebookCommentsPlugin
PostedinAudioSynthesis permalink[http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/]
AboutDemofox
I'magameandengineprogrammeratBlizzardEntertainmentandhavebeenmakinggamessince1990(startingoutwith
QBasicandTI85games)Myshippedtitlesinclude:*HeroesoftheStorm*StarCraftII:HeartoftheSwarm&Legacyof
thevoid*InsanelyTwistedShadowPlanet(PC)*GothamCityImpostors(PC,360,PS3)*LineRider(PC,Wii,DS)Ialso
likehiking,makingmusic,learningcoolnewstuffandattemptingtheimpossible.
ViewallpostsbyDemofox
http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 11/11